src/stream/file/Makefile \
src/stream/tcp/test/Makefile \
src/network_inspectors/Makefile \
+src/network_inspectors/appid/Makefile \
+src/network_inspectors/appid/client_plugins/Makefile \
+src/network_inspectors/appid/detector_plugins/Makefile \
+src/network_inspectors/appid/service_plugins/Makefile \
+src/network_inspectors/appid/test/Makefile \
+src/network_inspectors/appid/util/Makefile \
src/network_inspectors/arp_spoof/Makefile \
src/network_inspectors/binder/Makefile \
src/network_inspectors/normalize/Makefile \
if STATIC_INSPECTORS
lib_list = \
+network_inspectors/appid/libappid.a \
+network_inspectors/appid/client_plugins/libappid_client_plugins.a \
+network_inspectors/appid/detector_plugins/libappid_detector_plugins.a \
+network_inspectors/appid/service_plugins/libappid_service_plugins.a \
+network_inspectors/appid/util/libappid_util.a \
network_inspectors/arp_spoof/libarp_spoof.a \
network_inspectors/packet_capture/libpacket_capture.a \
service_inspectors/back_orifice/libback_orifice.a \
#include "protocols/packet.h"
#include "sfip/sf_ip.h"
-unsigned FlowData:: flow_id = 0;
+unsigned FlowData::flow_id = 0;
FlowData::FlowData(unsigned u, Inspector* ph)
{
#define DEBUG_DCE_TCP 0x0080000000000000LL
#define DEBUG_DCE_SMB 0x0100000000000000LL
#define DEBUG_DCE_COMMON 0x0200000000000000LL
+#define DEBUG_APPID 0x0400000000000000LL
#ifdef PIGLET
#define DEBUG_PIGLET 0x0400000000000000LL
+add_subdirectory(appid)
add_subdirectory(arp_spoof)
add_subdirectory(binder)
add_subdirectory(normalize)
target_link_libraries( network_inspectors
${STATIC_INSPECTOR_LIBS}
+ appid
+ appid_client_plugins
+ appid_detector_plugins
+ appid_service_plugins
+ appid_util
binder
normalize
perf_monitor
port_scan
- reputation
+ reputation
stream_tcp
)
#port_scan/libport_scan.a
SUBDIRS = \
+appid \
arp_spoof \
binder \
normalize \
--- /dev/null
+- Remove 'struct ' before parameter and variable declarations.
+- If 'static' and 'inline' are on separate lines, put them on one line.
+- `SFSnortPacket` -> `Packet`
+- `p->src_port` -> `p->ptrs.sp`
+- `p->dst_port` -> `p->ptrs.dp`
+- `GET_DST_IP(p)` -> `p->ptrs.ip_api.get_dst()`
+- `GET_SRC_IP(p)` -> `p->ptrs.ip_api.get_src()`
+- `p->pkt_header` -> `p->pkth`
+- `p->payload` -> `p->data`
+- `p->payload_size` -> `p->dsize`
+- `SF_SO_PUBLIC` -> `SO_PUBLIC`
+- `p->ether_header` -> `layer::get_eth_layer(p)`
+- `p->tcp_header->flags & TCPHEADER_*` -> `p->ptrs.tcph->are_flags_set(TH_*)`
+- `ntohs(pkt->tcp_header->urgent_pointer)` -> `p->ptrs.tcph->urp()`
+- `_dpd.searchAPI` -> `SeachTool`
+- _dpd.logMsg -> LogMessage
+- _dpd.errMsg -> ErrorMessage
+- _dpd.fatalMsg -> FatalError
+- _dpd.debugMsg -> If there are printf args, then use DebugFormat. if not,
+ use DebugMessage.
+- _dpd.addProtocolReference(name) -> AddProtocolReference(name)
+
+Non-reentrant functions in use:
+
+* strerror strerror_r
+* strtok strtok_r
+
+Other non-reentrant functions:
+
+* asctime asctime_r
+* ctime ctime_r
+* getgrnam getgrnam_r
+* gethostbyaddr gethostbyname_r
+* gethostbyname gethostbyaddr_r
+* gethostent gethostent_r
+* getprotobyname getprotobyname_r
+* getprotobynumber getprotobynumber_r
+* getpwnam getpwnam_r
+* getpwuid getpwuid_r
+* getservbyname getservbyname_r
+* gmtime gmtime_r
+* hcreate hcreate_r
+* hsearch hsearch_r
+* hdestroy hdestroy_r
+* localtime localtime_r
+* pcap_strerror strerror_r
+* random random_r
+* readdir readdir_r
+* ttyname ttyname_r
+
+special case; inet_ntoa_r not available on linux;
+inet_ntoa is redefined to sfip_ntoa()
+* inet_ntoa +inet_ntop
+
+# mac os doesn't have random_r and srandom_r. And rand_r # is weak.
+
+* random random_r
+* rand rand_r
+* srandom srandom_r
--- /dev/null
+set ( APPID_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR} )
+
+add_subdirectory(client_plugins)
+add_subdirectory(detector_plugins)
+add_subdirectory(service_plugins)
+add_subdirectory(util)
+add_subdirectory ( test )
+
+set ( APPID_SOURCES
+ app_forecast.cc
+ app_forecast.h
+ appid_api.cc
+ appid_api.h
+ appid_config.cc
+ appid_config.h
+ appid_flow_data.cc
+ appid_flow_data.h
+ appid.h
+ appid_inspector.cc
+ appid_inspector.h
+ appid_module.cc
+ appid_module.h
+ appid_stats.cc
+ appid_stats.h
+ app_info_table.cc
+ app_info_table.h
+ application_ids.h
+ dns_defs.h
+ flow_error.h
+ fw_appid.cc
+ fw_appid.h
+ host_port_app_cache.cc
+ host_port_app_cache.h
+ http_common.h
+ length_app_cache.cc
+ length_app_cache.h
+ lua_detector_api.cc
+ lua_detector_api.h
+ lua_detector_flow_api.cc
+ lua_detector_flow_api.h
+ lua_detector_module.cc
+ lua_detector_module.h
+ lua_detector_util.h
+ service_state.cc
+ service_state.h
+ sfaddr_temp.h
+ thirdparty_appid_api.h
+ thirdparty_appid_types.h
+ thirdparty_appid_utils.cc
+ thirdparty_appid_utils.h
+ )
+
+add_library ( appid STATIC
+ ${APPID_SOURCES}
+)
+
+target_include_directories ( appid PRIVATE ${APPID_INCLUDE_DIR} )
+
+target_link_libraries ( appid
+ appid_client_plugins
+ appid_detector_plugins
+ appid_service_plugins
+)
+
+# FIXIT-H: Add unit tests
+
+#install (FILES ${APPID_INCLUDES}
+# DESTINATION "${INCLUDE_INSTALL_PATH}/appid"
+#)
--- /dev/null
+FILENAME PORTING STATUS
+-------- --------------
+# Syntax:
+# - lines beginning with '#' are comments
+# - anything after a '#' on a line is a comment
+# - unprepped: <filename>
+# - in-progress: <filename> WORKING <cisco_username>
+# - not in-progress but not complete: <filename> PARTIAL <cisco_username>
+# - completed: <filename> DONE <cisco_username>
+#
+# A file should set to PARTIAL when an updated version is committed but not all the work is done
+# and the developer committing is not going to continue making changes, i.e. someone else can
+# grab it and make more changes.
+#
+# you can add a short note/comment after the file if necessary
+# example:
+# dns_defs.h WORKING jocornet # fixed everything but includes
+#
+
+#FILENAME STATUS USER
+#-------- ------ ----
+
+appid.h DONE jocornet
+app_forecast.cc DONE jocornet
+app_forecast.h DONE jocornet
+app_info_table.cc DONE jocornet
+app_info_table.h DONE jocornet
+appid_api.cc DONE jocornet
+appid_api.h DONE jocornet
+appid_config.cc DONE jocornet
+appid_config.h DONE jocornet
+appid_stats.cc DONE jocornet
+appid_stats.h DONE jocornet
+application_ids.h DONE jocornet
+common_app_matcher.cc DONE jocornet
+common_app_matcher.h DONE jocornet
+dns_defs.h DONE jocornet
+appid_flow_data.cc DONE jocornet
+appid_flow_data.h DONE jocornet
+flow_error.h DONE jocornet
+fw_appid.cc DONE jocornet
+fw_appid.h DONE eborgoyn
+host_port_app_cache.cc DONE eborgoyn
+host_port_app_cache.h DONE eborgoyn
+http_common.h DONE eborgoyn
+length_app_cache.cc DONE eborgoyn
+length_app_cache.h DONE eborgoyn
+lua_detector_api.cc DONE jocornet
+lua_detector_api.h DONE eborgoyn
+lua_detector_flow_api.cc DONE eborgoyn # pending sfaddr -> sfip conversion
+lua_detector_flow_api.h DONE eborgoyn
+lua_detector_module.cc DONE eborgoyn # compiles, but needs a ton of work
+lua_detector_module.h DONE eborgoyn
+lua_detector_util.h DONE eborgoyn # added by jocornet
+service_state.cc DONE eborgoyn
+service_state.h DONE eborgoyn
+sfaddr_temp.h DONE eborgoyn
+spp_appid.cc DONE huica
+spp_appid.h DONE huica
+thirdparty_appid_api.h DONE huica
+thirdparty_appid_types.h DONE huica
+thirdparty_appid_utils.cc DONE huica
+thirdparty_appid_utils.h DONE huica
+client_plugins/client_app_aim.cc DONE huica
+client_plugins/client_app_aim.h DONE huica
+client_plugins/client_app_api.h DONE huica
+client_plugins/client_app_base.cc DONE huica
+client_plugins/client_app_base.h DONE huica
+client_plugins/client_app_bit.cc DONE huica
+client_plugins/client_app_bit_tracker.cc DONE huica
+client_plugins/client_app_config.h DONE huica
+client_plugins/client_app_msn.cc DONE huica
+client_plugins/client_app_msn.h DONE huica
+client_plugins/client_app_rtp.cc DONE mdagon
+client_plugins/client_app_smtp.cc DONE mdagon
+client_plugins/client_app_smtp.h DONE mdagon
+client_plugins/client_app_ssh.cc DONE mdagon
+client_plugins/client_app_timbuktu.cc DONE mdagon
+client_plugins/client_app_tns.cc DONE mdagon
+client_plugins/client_app_vnc.cc DONE mdagon
+client_plugins/client_app_ym.cc DONE mdagon
+client_plugins/client_app_ym.h DONE mdagon
+detector_plugins/detector_api.h DONE mdagon
+detector_plugins/detector_base.cc DONE mdagon
+detector_plugins/detector_base.h DONE mdagon
+detector_plugins/detector_dns.cc DONE mdagon
+detector_plugins/detector_dns.h DONE mdagon
+detector_plugins/detector_http.cc DONE mdagon # sfaddr_t problems (notably 2x `sfip_set_raw`)
+detector_plugins/detector_http.h DONE mdagon
+detector_plugins/detector_imap.cc DONE mialtize
+detector_plugins/detector_kerberos.cc DONE mialtize
+detector_plugins/detector_pattern.cc DONE mialtize
+detector_plugins/detector_pattern.h DONE mialtize
+detector_plugins/detector_pop3.cc DONE mialtize
+detector_plugins/detector_sip.cc DONE mialtize
+detector_plugins/detector_sip.h DONE mialtize
+detector_plugins/http_url_patterns.cc DONE mialtize
+detector_plugins/http_url_patterns.h DONE mialtize
+service_plugins/dcerpc.cc DONE mialtize
+service_plugins/dcerpc.h DONE mialtize
+service_plugins/service_api.h DONE mialtize
+service_plugins/service_battle_field.cc DONE mialtize
+service_plugins/service_battle_field.h DONE mialtize
+service_plugins/service_bgp.cc DONE mialtize
+service_plugins/service_bgp.h DONE mialtize
+service_plugins/service_base.cc DONE davmcphe # SF_LIST and sfip_t/sfaddr_t badness
+service_plugins/service_base.h DONE davmcphe
+service_plugins/service_bit.cc DONE davmcphe
+service_plugins/service_bootp.cc DONE davmcphe
+service_plugins/service_bootp.h DONE davmcphe
+service_plugins/service_config.h DONE davmcphe
+service_plugins/service_dcerpc.cc DONE davmcphe
+service_plugins/service_dcerpc.h DONE davmcphe
+service_plugins/service_direct_connect.cc DONE davmcphe
+service_plugins/service_direct_connect.h DONE davmcphe
+service_plugins/service_flap.cc DONE davmcphe
+service_plugins/service_flap.h DONE davmcphe
+service_plugins/service_ftp.cc DONE davmcphe
+service_plugins/service_ftp.h DONE davmcphe
+service_plugins/service_irc.cc DONE davmcphe
+service_plugins/service_irc.h DONE davmcphe
+service_plugins/service_lpr.cc DONE davmcphe
+service_plugins/service_lpr.h DONE rucombs
+service_plugins/service_mdns.cc DONE rucombs # name change, searchAPI -> SearchTool not completed
+service_plugins/service_mdns.h DONE rucombs # name change
+service_plugins/service_mysql.cc DONE rucombs
+service_plugins/service_mysql.h DONE rucombs
+service_plugins/service_netbios.cc DONE rucombs
+service_plugins/service_netbios.h DONE rucombs
+service_plugins/service_nntp.cc DONE rucombs
+service_plugins/service_nntp.h DONE rucombs
+service_plugins/service_ntp.cc DONE rucombs
+service_plugins/service_ntp.h DONE rucombs
+service_plugins/service_radius.cc DONE rucombs
+service_plugins/service_radius.h DONE rucombs
+service_plugins/service_rexec.cc DONE rucombs
+service_plugins/service_rexec.h DONE rucombs
+service_plugins/service_rfb.cc DONE rucombs
+service_plugins/service_rfb.h WORKING stechew
+service_plugins/service_rlogin.cc WORKING stechew
+service_plugins/service_rlogin.h WORKING stechew
+service_plugins/service_rpc.cc WORKING stechew
+service_plugins/service_rpc.h WORKING stechew
+service_plugins/service_rshell.cc WORKING stechew
+service_plugins/service_rshell.h WORKING stechew
+service_plugins/service_rsync.cc WORKING stechew
+service_plugins/service_rsync.h WORKING stechew
+service_plugins/service_rtmp.cc WORKING stechew
+service_plugins/service_rtmp.h WORKING stechew
+service_plugins/service_smtp.cc WORKING stechew
+service_plugins/service_smtp.h WORKING stechew
+service_plugins/service_snmp.cc WORKING stechew
+service_plugins/service_snmp.h WORKING stechew
+service_plugins/service_ssh.cc WORKING stechew
+service_plugins/service_ssh.h DONE thopeter
+service_plugins/service_ssl.cc DONE thopeter
+service_plugins/service_ssl.h DONE thopeter
+service_plugins/service_telnet.cc DONE thopeter
+service_plugins/service_telnet.h DONE thopeter
+service_plugins/service_tftp.cc DONE thopeter
+service_plugins/service_tftp.h DONE thopeter
+service_plugins/service_timbuktu.cc DONE thopeter
+service_plugins/service_tns.cc DONE thopeter
+service_plugins/service_util.h DONE thopeter
+test/appid_tests.cc PARTIAL thopeter
+test/external_apis.cc PARTIAL viroemer
+test/external_apis.h DONE thopeter
+test/mpse.cc PARTIAL thopeter
+test/session_file.cc PARTIAL thopeter
+test/session_file.h DONE thopeter
+test/sf_iph.cc PARTIAL thopeter
+util/common_util.h DONE viroemer
+util/fw_avltree.cc DONE viroemer
+util/fw_avltree.h DONE viroemer
+util/ip_funcs.cc DONE viroemer
+util/ip_funcs.h DONE viroemer
+util/network_set.cc DONE viroemer
+util/network_set.h DONE viroemer
+util/output_file.cc DONE viroemer
+util/output_file.h DONE viroemer
+util/sf_mlmp.cc DONE viroemer
+util/sf_mlmp.h DONE viroemer
+util/sf_multi_mpse.cc DONE viroemer
+util/sf_multi_mpse.h DONE viroemer
+util/sfksearch.cc DONE viroemer
+util/sfksearch.h DONE viroemer
+util/sfutil.cc DONE viroemer
+util/sfutil.h DONE viroemer
--- /dev/null
+
+AM_CPPFLAGS+=-I$(top_srcdir)/src/network_inspectors/appid
+
+file_list = \
+app_forecast.cc \
+app_forecast.h \
+appid_api.cc \
+appid_api.h \
+appid_config.cc \
+appid_config.h \
+appid_flow_data.cc \
+appid_flow_data.h \
+appid.h \
+appid_inspector.cc \
+appid_inspector.h \
+appid_module.cc \
+appid_module.h \
+appid_stats.cc \
+appid_stats.h \
+app_info_table.cc \
+app_info_table.h \
+application_ids.h \
+dns_defs.h \
+flow_error.h \
+fw_appid.cc \
+fw_appid.h \
+host_port_app_cache.cc \
+host_port_app_cache.h \
+http_common.h \
+length_app_cache.cc \
+length_app_cache.h \
+lua_detector_api.cc \
+lua_detector_api.h \
+lua_detector_flow_api.cc \
+lua_detector_flow_api.h \
+lua_detector_module.cc \
+lua_detector_module.h \
+lua_detector_util.h \
+service_state.cc \
+service_state.h \
+sfaddr_temp.h \
+thirdparty_appid_api.h \
+thirdparty_appid_types.h \
+thirdparty_appid_utils.cc \
+thirdparty_appid_utils.h
+
+noinst_LIBRARIES = libappid.a
+libappid_a_SOURCES = $(file_list)
+
+SUBDIRS = \
+client_plugins \
+detector_plugins \
+service_plugins \
+util
+
+#if BUILD_CPPUTESTS
+#SUBDIRS += test
+#endif
+
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// app_forecast.cc author Sourcefire Inc.
+
+#include "app_forecast.h"
+
+#include "hash/sfxhash.h"
+#include "time/packet_time.h"
+
+#include "application_ids.h"
+
+static AFActKey master_key;
+
+static inline void rekeyMasterAFActKey(Packet* p, int dir, ApplicationId forecast)
+{
+ const sfip_t* src = dir ? p->ptrs.ip_api.get_dst() : p->ptrs.ip_api.get_src();
+
+ for (int i = 0; i < 4; i++)
+ master_key.ip[i] = src->ip32[i];
+
+ master_key.forecast = forecast;
+}
+
+void checkSessionForAFIndicator(
+ Packet* p, int dir, const AppIdConfig* pConfig, ApplicationId 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 = packet_time();
+ test_active_value->target = ind_element->target;
+ return;
+ }
+
+ AFActVal new_active_value;
+ new_active_value.target = ind_element->target;
+ new_active_value.last = packet_time();
+
+ sfxhash_add(pConfig->AF_actives, &master_key, &new_active_value);
+}
+
+AppId checkSessionForAFForecast(
+ AppIdData* session, Packet* p, int dir, const AppIdConfig* pConfig, ApplicationId 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 = packet_time() - 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;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// app_forecast.h author Sourcefire Inc.
+
+#ifndef APP_FORECAST_H
+#define APP_FORECAST_H
+
+// AppId flow forcasting data structures and methods
+//
+
+#include <time.h>
+#include "appid_api.h"
+#include "appid_config.h"
+#include "protocols/packet.h"
+
+#include "appid_flow_data.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.
+
+class AppIdConfig;
+enum ApplicationId : int32_t;
+
+struct AFElement
+{
+ ApplicationId indicator;
+ ApplicationId forecast;
+ ApplicationId target;
+};
+
+struct AFActKey
+{
+ uint32_t ip[4];
+ ApplicationId forecast;
+};
+
+struct AFActVal
+{
+ ApplicationId target;
+ time_t last;
+};
+
+void checkSessionForAFIndicator(Packet*, int, const AppIdConfig*, ApplicationId);
+AppId checkSessionForAFForecast(AppIdData*, Packet*, int, const AppIdConfig*, ApplicationId);
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// app_info_table.cc author Sourcefire Inc.
+
+#include "app_info_table.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "application_ids.h"
+
+#include "log/messages.h"
+#include "hash/sfghash.h"
+#include "main/snort_debug.h"
+#include "target_based/snort_protocols.h"
+#include "utils/util.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
+#define CONF_SEPARATORS "\t\n\r"
+#define MIN_MAX_TP_FLOW_DEPTH 1
+#define MAX_MAX_TP_FLOW_DEPTH 1000000
+
+struct DynamicArray
+{
+ AppInfoTableEntry** table;
+ size_t indexStart;
+ size_t indexCurrent;
+ size_t usedCount;
+ size_t allocatedCount;
+ size_t stepSize;
+};
+
+static inline DynamicArray* dynamicArrayCreate(unsigned indexStart)
+{
+ DynamicArray* array;
+
+ array = (DynamicArray*)snort_calloc(sizeof(DynamicArray));
+ array->stepSize = 1;
+ array->indexStart = indexStart;
+ return array;
+}
+
+static inline void dynamicArrayDestroy(DynamicArray* array)
+{
+ unsigned i;
+ AppInfoTableEntry* entry;
+
+ if (!array)
+ return;
+ for (i = 0; i < array->usedCount; i++)
+ {
+ entry = array->table[i];
+ snort_free(entry->appName);
+ snort_free(entry);
+ }
+
+ // FIXIT - array table is still alloc'ed with calloc/realloc
+ free(array->table);
+ snort_free(array);
+}
+
+static inline void dynamicArraySetIndex(DynamicArray* array, unsigned index,
+ AppInfoTableEntry* data)
+{
+ if (index >= array->indexStart && index < (array->indexStart + array->usedCount))
+ array->table[index - array->indexStart] = data;
+}
+
+static inline AppInfoTableEntry* dynamicArrayGetIndex(DynamicArray* array, unsigned index)
+{
+ if (index >= array->indexStart && index < (array->indexStart + array->usedCount))
+ return array->table[index - array->indexStart];
+ return nullptr;
+}
+
+static inline bool dynamicArrayCreateIndex(DynamicArray* array, unsigned* index)
+{
+ if (array->usedCount == array->allocatedCount)
+ {
+ AppInfoTableEntry** tmp =
+ (AppInfoTableEntry**)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(DynamicArray* array)
+{
+ AppInfoTableEntry* entry;
+ for (array->indexCurrent = 0; array->indexCurrent < array->usedCount; array->indexCurrent++)
+ {
+ if ((entry = array->table[array->indexCurrent]))
+ return entry;
+ }
+ return nullptr;
+}
+
+static inline void* dynamicArrayGetNext(DynamicArray* array)
+{
+ AppInfoTableEntry* entry;
+ for (array->indexCurrent++; array->indexCurrent < array->usedCount; array->indexCurrent++)
+ {
+ if ((entry = array->table[array->indexCurrent]))
+ return entry;
+ }
+ return nullptr;
+}
+
+// End of Dynamic array
+SFGHASH* appNameHashInit()
+{
+ SFGHASH* appNameHash;
+ appNameHash = sfghash_new(65, 0, 0 /* alloc copies of lowercased keys */, nullptr);
+ if (!appNameHash)
+ {
+ FatalError("AppNameHash: Failed to Initialize\n");
+ }
+ return appNameHash;
+}
+
+void appNameHashFini(SFGHASH* appNameHash)
+{
+ if (appNameHash)
+ {
+ sfghash_delete(appNameHash);
+ }
+}
+
+static inline char* strdupToLower(const char* source)
+{
+ char* dest = snort_strdup(source);
+ char* lcd = dest;
+
+ while(*lcd)
+ {
+ *lcd = tolower(*lcd);
+ lcd++;
+ }
+
+ 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)))
+ {
+ DebugFormat(DEBUG_INSPECTOR, "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)
+ {
+ ErrorMessage("App name, \"%s\", is a duplicate of \"%s\" and has been ignored.\n",
+ appName, tableEntry->appName);
+ }
+ else
+ {
+ ErrorMessage("App name, \"%s\", has been ignored. Hash key \"%s\" is not unique.\n",
+ appName, searchName);
+ }
+ }
+ snort_free(searchName);
+}
+
+void* appNameHashFind(SFGHASH* appNameHash, const char* appName)
+{
+ void* data;
+ char* searchName;
+
+ if (!appName || !appNameHash)
+ return nullptr;
+
+ searchName = strdupToLower(appName);
+ if (!searchName)
+ return nullptr;
+
+ data = sfghash_find(appNameHash, searchName);
+
+ snort_free(searchName);
+
+ return data;
+}
+
+// End of appName hash
+
+static void appIdConfLoad(const char* path);
+
+static unsigned int getAppIdStaticIndex(AppId 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(AppId appId, const AppIdConfig* pConfig)
+{
+ AppId tmp;
+ if ((tmp = getAppIdStaticIndex(appId)))
+ return pConfig->AppInfoTable[tmp];
+ return dynamicArrayGetIndex(pConfig->AppInfoTableDyn, appId);
+}
+
+AppInfoTableEntry* appInfoEntryCreate(const char* appName, AppIdConfig* pConfig)
+{
+ AppId appId;
+ AppInfoTableEntry* entry;
+
+ if (!appName || strlen(appName) >= MAX_EVENT_APPNAME_LEN)
+ {
+ ErrorMessage("Appname invalid or too long: %s\n", appName);
+ return nullptr;
+ }
+
+ entry = static_cast<decltype(entry)>(appNameHashFind(pConfig->AppNameHash, appName));
+
+ if (!entry)
+ {
+ if (!dynamicArrayCreateIndex(pConfig->AppInfoTableDyn, (uint32_t*)&appId))
+ return nullptr;
+
+ entry = static_cast<decltype(entry)>(snort_calloc(sizeof(AppInfoTableEntry)));
+ entry->appId = appId;
+ entry->serviceId = entry->appId;
+ entry->clientId = entry->appId;
+ entry->payloadId = entry->appId;
+ entry->appName = snort_strdup(appName);
+ dynamicArraySetIndex(pConfig->AppInfoTableDyn, appId, entry);
+ }
+ return entry;
+}
+
+void appInfoTableInit(const char* path, AppIdConfig* pConfig)
+{
+ FILE* tableFile;
+ const char* token;
+ char buf[MAX_TABLE_LINE_LEN];
+ AppInfoTableEntry* entry;
+ AppId appId;
+ uint32_t clientId, serviceId, payloadId;
+ char filepath[PATH_MAX];
+ char* appName;
+ char* snortName=nullptr;
+ char* context;
+
+ pConfig->AppInfoTableDyn = dynamicArrayCreate(SF_APPID_DYNAMIC_MIN);
+
+ snprintf(filepath, sizeof(filepath), "%s/odp/%s", path, APP_MAPPING_FILE);
+
+ tableFile = fopen(filepath, "r");
+ if (tableFile == nullptr)
+ {
+ ErrorMessage("Could not open RnaAppMapping Table file: %s\n", filepath);
+ return;
+ }
+
+ DebugFormat(DEBUG_INSPECTOR, "AppInfo read from %s\n", filepath);
+
+ while (fgets(buf, sizeof(buf), tableFile))
+ {
+ token = strtok_r(buf, CONF_SEPARATORS, &context);
+ if (!token)
+ {
+ ErrorMessage("Could not read id for Rna Id\n");
+ continue;
+ }
+
+ appId = strtol(token, nullptr, 10);
+
+ token = strtok_r(nullptr, CONF_SEPARATORS, &context);
+ if (!token)
+ {
+ ErrorMessage("Could not read appName. Line %s\n", buf);
+ continue;
+ }
+
+ appName = snort_strdup(token);
+ token = strtok_r(nullptr, CONF_SEPARATORS, &context);
+ if (!token)
+ {
+ ErrorMessage("Could not read service id for Rna Id\n");
+ snort_free(appName);
+ continue;
+ }
+
+ serviceId = strtol(token, nullptr, 10);
+
+ token = strtok_r(nullptr, CONF_SEPARATORS, &context);
+ if (!token)
+ {
+ ErrorMessage("Could not read client id for Rna Id\n");
+ snort_free(appName);
+ continue;
+ }
+
+ clientId = strtol(token, nullptr, 10);
+
+ token = strtok_r(nullptr, CONF_SEPARATORS, &context);
+ if (!token)
+ {
+ ErrorMessage("Could not read payload id for Rna Id\n");
+ snort_free(appName);
+ continue;
+ }
+
+ payloadId = strtol(token, nullptr, 10);
+
+ /* snort service key, if it exists */
+ token = strtok_r(nullptr, CONF_SEPARATORS, &context);
+ if (token)
+ snortName = snort_strdup(token);
+
+ entry = static_cast<decltype(entry)>(snort_calloc(sizeof(AppInfoTableEntry)));
+ entry->next = pConfig->AppInfoList;
+ pConfig->AppInfoList = entry;
+ entry->snortId = AddProtocolReference(snortName);
+ snort_free(snortName);
+ snortName = nullptr;
+ 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. */
+ pAppidActiveConfig->mod_config->rtmp_max_packets = 15;
+ pAppidActiveConfig->mod_config->mdns_user_reporting = 1;
+ pAppidActiveConfig->mod_config->dns_host_reporting = 1;
+ pAppidActiveConfig->mod_config->max_tp_flow_depth = 5;
+ pAppidActiveConfig->mod_config->http2_detection_enabled = 0;
+
+ snprintf(filepath, sizeof(filepath), "%s/odp/%s", path, APP_CONFIG_FILE);
+ appIdConfLoad (filepath);
+ snprintf(filepath, sizeof(filepath), "%s/custom/%s", path, USR_CONFIG_FILE);
+ appIdConfLoad (filepath);
+}
+
+void appInfoTableFini(AppIdConfig* pConfig)
+{
+ AppInfoTableEntry* entry;
+
+ while ((entry = pConfig->AppInfoList))
+ {
+ pConfig->AppInfoList = entry->next;
+ snort_free(entry->appName);
+ snort_free(entry);
+ }
+
+ dynamicArrayDestroy(pConfig->AppInfoTableDyn);
+ pConfig->AppInfoTableDyn = nullptr;
+
+ appNameHashFini(pConfig->AppNameHash);
+}
+
+void appInfoTableDump(AppIdConfig* pConfig)
+{
+ AppInfoTableEntry* entry;
+ AppId appId;
+
+ ErrorMessage("Cisco provided detectors:\n");
+ for (appId = 1; appId < SF_APPID_MAX; appId++)
+ {
+ entry = pConfig->AppInfoTable[appId];
+ if (entry)
+ ErrorMessage("%s\t%d\t%s\n", entry->appName, entry->appId, (entry->flags &
+ APPINFO_FLAG_ACTIVE) ? "active" : "inactive");
+ }
+ ErrorMessage("User provided detectors:\n");
+ for (entry = (decltype(entry))dynamicArrayGetFirst(pConfig->AppInfoTableDyn); entry; entry =
+ (decltype(entry))dynamicArrayGetNext(pConfig->AppInfoTableDyn))
+ {
+ ErrorMessage("%s\t%d\t%s\n", entry->appName, entry->appId, (entry->flags &
+ APPINFO_FLAG_ACTIVE) ? "active" : "inactive");
+ }
+}
+
+AppId appGetAppFromServiceId(uint32_t appId, AppIdConfig* pConfig)
+{
+ AppInfoTableEntry* entry;
+ AppId tmp;
+
+ if ((tmp = getAppIdStaticIndex(appId)))
+ entry = pConfig->AppInfoTableByService[tmp];
+ else
+ entry = dynamicArrayGetIndex(pConfig->AppInfoTableDyn, appId);
+
+ return entry ? entry->appId : APP_ID_NONE;
+}
+
+AppId appGetAppFromClientId(uint32_t appId, AppIdConfig* pConfig)
+{
+ AppInfoTableEntry* entry;
+ AppId tmp;
+
+ if ((tmp = getAppIdStaticIndex(appId)))
+ entry = pConfig->AppInfoTableByClient[tmp];
+ else
+ entry = dynamicArrayGetIndex(pConfig->AppInfoTableDyn, appId);
+
+ return entry ? entry->appId : APP_ID_NONE;
+}
+
+AppId appGetAppFromPayloadId(uint32_t appId, AppIdConfig* pConfig)
+{
+ AppInfoTableEntry* entry;
+ AppId 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;
+ AppIdConfig* pConfig = pAppidActiveConfig;
+ AppId tmp;
+
+ if ((tmp = getAppIdStaticIndex(appId)))
+ entry = pConfig->AppInfoTable[tmp];
+ else
+ entry = dynamicArrayGetIndex(pConfig->AppInfoTableDyn, appId);
+
+ return entry ? entry->appName : nullptr;
+}
+
+int32_t appGetAppId(const char* appName)
+{
+ AppInfoTableEntry* entry;
+ AppIdConfig* pConfig = pAppidActiveConfig;
+
+ entry = (decltype(entry))appNameHashFind(pConfig->AppNameHash, appName);
+ return entry ? entry->appId : 0;
+}
+
+void appInfoSetActive(AppId appId, bool active)
+{
+ AppInfoTableEntry* entry = nullptr;
+ AppIdConfig* pConfig = pAppidActiveConfig;
+ AppId 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
+ {
+ ErrorMessage("AppInfo: AppId %d is UNKNOWN\n", appId);
+ }
+}
+
+static void appIdConfLoad(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;
+ AppIdConfig* pConfig = pAppidActiveConfig;
+ int max_tp_flow_depth;
+ char* context;
+
+ config_file = fopen(path, "r");
+ if (config_file == nullptr)
+ return;
+
+ DebugFormat(DEBUG_INSPECTOR, "Loading configuration file %s\n", path);
+
+ while (fgets(buf, sizeof(buf), config_file) != nullptr)
+ {
+ line++;
+ token = strtok_r(buf, CONF_SEPARATORS, &context);
+ if (token == nullptr)
+ {
+ ErrorMessage("Could not read configuration at line %s:%u\n", path, line);
+ continue;
+ }
+ conf_type = token;
+
+ token = strtok_r(nullptr, CONF_SEPARATORS, &context);
+ if (token == nullptr)
+ {
+ ErrorMessage("Could not read configuration value at line %s:%u\n", path, line);
+ continue;
+ }
+ conf_key = token;
+
+ token = strtok_r(nullptr, CONF_SEPARATORS, &context);
+ if (token == nullptr)
+ {
+ ErrorMessage("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)
+ {
+ DebugFormat(DEBUG_INSPECTOR,
+ "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
+ {
+ DebugFormat(DEBUG_INSPECTOR,
+ "AppId: setting max thirdparty inspection flow depth to %d packets.\n",
+ max_tp_flow_depth);
+ pAppidActiveConfig->mod_config->max_tp_flow_depth = max_tp_flow_depth;
+ }
+ }
+ else if (!(strcasecmp(conf_key, "tp_allow_probes")))
+ {
+ if (!(strcasecmp(conf_val, "enabled")))
+ {
+ DebugMessage(DEBUG_INSPECTOR,
+ "AppId: TCP probes will be analyzed by NAVL.\n");
+
+ pAppidActiveConfig->mod_config->tp_allow_probes = 1;
+ }
+ }
+ else if (!(strcasecmp(conf_key, "tp_client_app")))
+ {
+ DebugFormat(DEBUG_INSPECTOR,
+ "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")))
+ {
+ DebugFormat(DEBUG_INSPECTOR,
+ "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_INSPECTOR,
+ "AppId: disabling safe search enforcement.\n"); );
+ pAppidActiveConfig->mod_config->disable_safe_search = 1;
+ }
+ }
+ else if (!(strcasecmp(conf_key, "ssl_squelch")))
+ {
+ DebugFormat(DEBUG_INSPECTOR,
+ "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")))
+ {
+ DebugFormat(DEBUG_INSPECTOR,
+ "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")))
+ {
+ DebugFormat(DEBUG_INSPECTOR,
+ "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")))
+ {
+ DebugMessage(DEBUG_INSPECTOR,
+ "AppId: HTTP UserID collection disabled.\n");
+ pAppidActiveConfig->mod_config->chp_userid_disabled = 1;
+ continue;
+ }
+ }
+ else if (!(strcasecmp(conf_key, "chp_body_collection")))
+ {
+ if (!(strcasecmp(conf_val, "disabled")))
+ {
+ DebugMessage(DEBUG_INSPECTOR,
+ "AppId: HTTP Body header reading disabled.\n");
+ pAppidActiveConfig->mod_config->chp_body_collection_disabled = 1;
+ continue;
+ }
+ }
+ else if (!(strcasecmp(conf_key, "chp_fflow")))
+ {
+ if (!(strcasecmp(conf_val, "disabled")))
+ {
+ DEBUG_WRAP(DebugMessage(DEBUG_INSPECTOR,
+ "AppId: HTTP future flow creation disabled.\n"); );
+ pAppidActiveConfig->mod_config->chp_fflow_disabled = 1;
+ continue;
+ }
+ }
+ else if (!(strcasecmp(conf_key, "ftp_userid")))
+ {
+ if (!(strcasecmp(conf_val, "disabled")))
+ {
+ DEBUG_WRAP(DebugMessage(DEBUG_INSPECTOR, "AppId: FTP userID disabled.\n"); );
+ pAppidActiveConfig->mod_config->ftp_userid_disabled = 1;
+ continue;
+ }
+ }
+ /* App Priority bit set*/
+ else if (!(strcasecmp(conf_key, "app_priority")))
+ {
+ int temp_appid;
+ temp_appid = strtol(conf_val, nullptr, 10);
+ token = strtok_r(nullptr, CONF_SEPARATORS, &context);
+ if (token == nullptr)
+ {
+ ErrorMessage("Could not read app_priority at line %u\n", line);
+ continue;
+ }
+ conf_val = token;
+ uint8_t temp_val;
+ temp_val = strtol(conf_val, nullptr, 10);
+ appInfoEntryPrioritySet (temp_appid, temp_val, pConfig);
+ DebugFormat(DEBUG_INSPECTOR,"AppId: %d Setting priority bit %d .\n",
+ temp_appid, temp_val);
+ }
+ else if (!(strcasecmp(conf_key, "referred_appId")))
+ {
+ if (!(strcasecmp(conf_val, "disabled")))
+ {
+ pAppidActiveConfig->mod_config->referred_appId_disabled = 1;
+ continue;
+ }
+ else if (!pAppidActiveConfig->mod_config->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_r(nullptr, CONF_SEPARATORS, &context)) != nullptr)
+ {
+ referred_app_index += sprintf(referred_app_list+referred_app_index, "%d ",
+ atoi(token));
+ appInfoEntryFlagSet(atoi(token), APPINFO_FLAG_REFERRED, pConfig);
+ }
+ DebugFormat(DEBUG_INSPECTOR,
+ "AppId: adding appIds to list of referred web apps: %s\n",
+ referred_app_list);
+ }
+ }
+ else if (!(strcasecmp(conf_key, "rtmp_max_packets")))
+ {
+ pAppidActiveConfig->mod_config->rtmp_max_packets = atoi(conf_val);
+ }
+ else if (!(strcasecmp(conf_key, "mdns_user_report")))
+ {
+ pAppidActiveConfig->mod_config->mdns_user_reporting = atoi(conf_val);
+ }
+ else if (!(strcasecmp(conf_key, "dns_host_report")))
+ {
+ pAppidActiveConfig->mod_config->dns_host_reporting = atoi(conf_val);
+ }
+ else if (!(strcasecmp(conf_key, "chp_body_max_bytes")))
+ {
+ pAppidActiveConfig->mod_config->chp_body_collection_max = atoi(conf_val);
+ }
+ else if (!(strcasecmp(conf_key, "ignore_thirdparty_appid")))
+ {
+ LogMessage("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")))
+ {
+ LogMessage("AppId: disabling internal HTTP/2 detection.\n");
+ pAppidActiveConfig->mod_config->http2_detection_enabled = 0;
+ }
+ else if (!(strcasecmp(conf_val, "enabled")))
+ {
+ LogMessage("AppId: enabling internal HTTP/2 detection.\n");
+ pAppidActiveConfig->mod_config->http2_detection_enabled = 1;
+ }
+ else
+ {
+ LogMessage("AppId: ignoring invalid option for http2_detection: %s\n",
+ conf_val);
+ }
+ }
+ }
+ }
+ fclose(config_file);
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// app_info_table.h author Sourcefire Inc.
+
+#ifndef APP_INFO_TABLE_H
+#define APP_INFO_TABLE_H
+
+#include <cstdint>
+
+#include "appid_api.h"
+#include "appid_config.h"
+
+#define APP_PRIORITY_DEFAULT 2
+
+struct RNAClientAppModule;
+struct RNAServiceElement;
+
+enum AppInfoFlags
+{
+ 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)
+};
+
+struct AppInfoTableEntry
+{
+ AppInfoTableEntry* next;
+ AppId appId;
+ uint32_t serviceId;
+ uint32_t clientId;
+ uint32_t payloadId;
+ int16_t snortId;
+ uint32_t flags;
+ const RNAClientAppModule* clntValidator;
+ const RNAServiceElement* svrValidator;
+ uint32_t priority;
+ char* appName;
+};
+
+void appInfoTableInit(const char* path, AppIdConfig*);
+void appInfoTableFini(AppIdConfig*);
+
+AppInfoTableEntry* appInfoEntryGet(AppId, const AppIdConfig*);
+AppInfoTableEntry* appInfoEntryCreate(const char* appName, AppIdConfig*);
+AppId appGetSnortIdFromAppId(AppId);
+void AppIdDumpStats(int exit_flag);
+void appInfoTableDump(AppIdConfig*);
+void appInfoSetActive(AppId, bool active);
+const char* appGetAppName(int32_t appId);
+int32_t appGetAppId(const char* appName);
+
+inline void appInfoEntryFlagSet(AppId appId, unsigned flags, AppIdConfig* pConfig)
+{
+ AppInfoTableEntry* entry = appInfoEntryGet(appId, pConfig);
+ if ( entry )
+ entry->flags |= flags;
+}
+
+inline void appInfoEntryFlagClear(AppId appId, unsigned flags, AppIdConfig* pConfig)
+{
+ AppInfoTableEntry* entry = appInfoEntryGet(appId, pConfig);
+ if ( entry )
+ entry->flags &= (~flags);
+}
+
+inline unsigned appInfoEntryFlagGet(AppId app_id, unsigned flags, AppIdConfig* pConfig)
+{
+ AppInfoTableEntry* entry = appInfoEntryGet(app_id, pConfig);
+ return entry ? entry->flags & flags : 0;
+}
+
+inline void appInfoEntryPrioritySet(AppId appId, unsigned priority, AppIdConfig* pConfig)
+{
+ AppInfoTableEntry* entry = appInfoEntryGet(appId, pConfig);
+ if ( entry )
+ entry->priority |= priority;
+}
+
+inline unsigned appInfoEntryPriorityGet(AppId app_id, AppIdConfig* pConfig)
+{
+ AppInfoTableEntry* entry = appInfoEntryGet(app_id, pConfig);
+ return entry ? entry->priority : 0;
+}
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// appid.h author Sourcefire Inc.
+
+#ifndef APPID_H
+#define APPID_H
+
+#include "thirdparty_appid_types.h"
+
+#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
+
+// FIXIT-H J stuff like this needs to be defined in config.h
+#define RESPONSE_CODE_PACKET_THRESHHOLD 0
+
+// FIXIT-L J properly scoped enum would be more appropriate
+enum APPID_SESSION_DIRECTION
+{
+ 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)
+};
+
+// FIXIT-M J this doesn't seem appropriate for an enum
+enum SERVICE_HOST_INFO_CODE
+{
+ SERVICE_HOST_INFO_NETBIOS_NAME = 1
+};
+
+// FIXIT-M J this should go in a separate header
+#define DHCP_OPTION55_LEN_MAX 255
+
+// FIXIT-M J this should go in a separate header
+#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)
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// appid_api.cc author Sourcefire Inc.
+
+#include "appid_api.h"
+
+#include "appid.h"
+#include "service_plugins/service_base.h"
+#include "app_info_table.h"
+#include "fw_appid.h"
+
+#include "utils/util.h"
+
+#define SSL_WHITELIST_PKT_LIMIT 20
+
+AppId getServiceAppId(AppIdData* appIdData)
+{
+ if (appIdData)
+ return pickServiceAppId(appIdData);
+
+ return APP_ID_NONE;
+}
+
+AppId getOnlyServiceAppId(AppIdData* appIdData)
+{
+ if (appIdData)
+ return pickOnlyServiceAppId(appIdData);
+
+ return APP_ID_NONE;
+}
+
+AppId getMiscAppId(AppIdData* appIdData)
+{
+ if (appIdData)
+ return pickMiscAppId(appIdData);
+
+ return APP_ID_NONE;
+}
+
+AppId getClientAppId(AppIdData* appIdData)
+{
+ if (appIdData)
+ return pickClientAppId(appIdData);
+
+ return APP_ID_NONE;
+}
+
+AppId getPayloadAppId(AppIdData* appIdData)
+{
+ if (appIdData)
+ return pickPayloadId(appIdData);
+
+ return APP_ID_NONE;
+}
+
+AppId getReferredAppId(AppIdData* appIdData)
+{
+ if (appIdData)
+ return pickReferredPayloadId(appIdData);
+
+ return APP_ID_NONE;
+}
+
+AppId getFwServiceAppId(AppIdData* appIdData)
+{
+ if (appIdData)
+ return fwPickServiceAppId(appIdData);
+
+ return APP_ID_NONE;
+}
+
+AppId getFwMiscAppId(AppIdData* appIdData)
+{
+ if (appIdData)
+ return fwPickMiscAppId(appIdData);
+
+ return APP_ID_NONE;
+}
+
+AppId getFwClientAppId(AppIdData* appIdData)
+{
+ if (appIdData)
+ return fwPickClientAppId(appIdData);
+
+ return APP_ID_NONE;
+}
+
+AppId getFwPayloadAppId(AppIdData* appIdData)
+{
+ if (appIdData)
+ return fwPickPayloadAppId(appIdData);
+
+ return APP_ID_NONE;
+}
+
+AppId getFwReferredAppId(AppIdData* appIdData)
+{
+ if (appIdData)
+ return fwPickReferredPayloadAppId(appIdData);
+
+ return APP_ID_NONE;
+}
+
+bool isSessionSslDecrypted(AppIdData* appIdData)
+{
+ if (appIdData)
+ return isFwSessionSslDecrypted(appIdData);
+
+ return false;
+}
+
+AppIdData* getAppIdData(void* lwssn)
+{
+ AppIdData* appIdData = (AppIdData*)(((Flow*)lwssn)->get_application_data(AppIdData::flow_id));
+
+ return (appIdData && appIdData->common.fsf_type.flow_type == APPID_SESSION_TYPE_NORMAL) ?
+ appIdData : nullptr;
+}
+
+bool IsAppIdInspectingSession(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;
+ }
+ }
+ return false;
+}
+
+char* getUserName(AppIdData* appIdData, AppId* service, bool* isLoginSuccessful)
+{
+ char* userName = nullptr;
+ if (appIdData)
+ {
+ userName = appIdData->username;
+ *service = appIdData->usernameService;
+ *isLoginSuccessful = getAppIdFlag(appIdData, APPID_SESSION_LOGIN_SUCCEEDED) ? true : false;
+ appIdData->username = nullptr; //transfer ownership to caller.
+ return userName;
+ }
+ return nullptr;
+}
+
+bool isAppIdAvailable(AppIdData* appIdData)
+{
+ if (appIdData)
+ {
+ if (getAppIdFlag(appIdData, APPID_SESSION_NO_TPI))
+ return true;
+ return TPIsAppIdAvailable(appIdData->tpsession);
+ }
+ return false;
+}
+
+char* getClientVersion(AppIdData* appIdData)
+{
+ return appIdData ? appIdData->clientVersion : nullptr;
+}
+
+uint64_t getAppIdSessionAttribute(AppIdData* appIdData, uint64_t flags)
+{
+ return appIdData ? getAppIdFlag(appIdData, flags) : 0;
+}
+
+APPID_FLOW_TYPE getFlowType(AppIdData* appIdData)
+{
+ return appIdData ? appIdData->common.fsf_type.flow_type : APPID_FLOW_TYPE_IGNORE;
+}
+
+void getServiceInfo(AppIdData* appIdData, char** serviceVendor, char** serviceVersion,
+ RNAServiceSubtype** serviceSubtype)
+{
+ if (appIdData)
+ {
+ *serviceVendor = appIdData->serviceVendor;
+ *serviceVersion = appIdData->serviceVersion;
+ *serviceSubtype = appIdData->subtype;
+ }
+ else
+ {
+ *serviceVendor = nullptr;
+ *serviceVersion = nullptr;
+ *serviceSubtype = nullptr;
+ }
+}
+
+short getServicePort(AppIdData* appIdData)
+{
+ if (appIdData)
+ return appIdData->service_port;
+ return 0;
+}
+
+char* getHttpUserAgent(AppIdData* appIdData)
+{
+ if (appIdData && appIdData->hsession)
+ return appIdData->hsession->useragent;
+ return nullptr;
+}
+
+char* getHttpHost(AppIdData* appIdData)
+{
+ if (appIdData && appIdData->hsession)
+ return appIdData->hsession->host;
+ return nullptr;
+}
+
+char* getHttpUrl(AppIdData* appIdData)
+{
+ if (appIdData && appIdData->hsession)
+ return appIdData->hsession->url;
+ return nullptr;
+}
+
+char* getHttpReferer(AppIdData* appIdData)
+{
+ if (appIdData && appIdData->hsession)
+ return appIdData->hsession->referer;
+ return nullptr;
+}
+
+char* getHttpNewUrl(AppIdData* appIdData)
+{
+ if (appIdData && appIdData->hsession)
+ return appIdData->hsession->new_field[REQ_URI_FID];
+ return nullptr;
+}
+
+char* getHttpUri(AppIdData* appIdData)
+{
+ if (appIdData && appIdData->hsession)
+ return appIdData->hsession->uri;
+ return nullptr;
+}
+
+char* getHttpResponseCode(AppIdData* appIdData)
+{
+ if (appIdData && appIdData->hsession)
+ return appIdData->hsession->response_code;
+ return nullptr;
+}
+
+char* getHttpCookie(AppIdData* appIdData)
+{
+ if (appIdData && appIdData->hsession)
+ return appIdData->hsession->cookie;
+ return nullptr;
+}
+
+char* getHttpNewCookie(AppIdData* appIdData)
+{
+ if (appIdData && appIdData->hsession)
+ return appIdData->hsession->new_field[REQ_COOKIE_FID];
+ return nullptr;
+}
+
+char* getHttpNewField(AppIdData* appIdData, HTTP_FIELD_ID fieldId)
+{
+ if (appIdData && appIdData->hsession && fieldId >= 0 && fieldId <= HTTP_FIELD_MAX)
+ return appIdData->hsession->new_field[fieldId];
+ return nullptr;
+}
+
+void freeHttpNewField(AppIdData* appIdData, HTTP_FIELD_ID fieldId)
+{
+ if (appIdData && appIdData->hsession && fieldId >= 0 && fieldId <= HTTP_FIELD_MAX &&
+ nullptr != appIdData->hsession->new_field[fieldId])
+ {
+ snort_free(appIdData->hsession->new_field[fieldId]);
+ appIdData->hsession->new_field[fieldId] = nullptr;
+ }
+}
+
+char* getHttpContentType(AppIdData* appIdData)
+{
+ if (appIdData && appIdData->hsession)
+ return appIdData->hsession->content_type;
+ return nullptr;
+}
+
+char* getHttpLocation(AppIdData* appIdData)
+{
+ if (appIdData && appIdData->hsession)
+ return appIdData->hsession->location;
+ return nullptr;
+}
+
+char* getHttpBody(AppIdData* appIdData)
+{
+ if (appIdData && appIdData->hsession)
+ return appIdData->hsession->body;
+ return nullptr;
+}
+
+char* getHttpReqBody(AppIdData* appIdData)
+{
+ if (appIdData && appIdData->hsession)
+ return appIdData->hsession->req_body;
+ return nullptr;
+}
+
+uint16_t getHttpUriOffset(AppIdData* appIdData)
+{
+ if (appIdData && appIdData->hsession)
+ return appIdData->hsession->uriOffset;
+ return 0;
+}
+
+uint16_t getHttpUriEndOffset(AppIdData* appIdData)
+{
+ if (appIdData && appIdData->hsession)
+ return appIdData->hsession->uriEndOffset;
+ return 0;
+}
+
+uint16_t getHttpCookieOffset(AppIdData* appIdData)
+{
+ if (appIdData && appIdData->hsession)
+ return appIdData->hsession->cookieOffset;
+ return 0;
+}
+
+uint16_t getHttpCookieEndOffset(AppIdData* appIdData)
+{
+ if (appIdData && appIdData->hsession)
+ return appIdData->hsession->cookieEndOffset;
+ return 0;
+}
+
+SEARCH_SUPPORT_TYPE getHttpSearch(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;
+}
+
+// FIXIT used to be sfaddr_t
+sfip_t* getHttpXffAddr(AppIdData* appIdData)
+{
+ if (appIdData && appIdData->hsession)
+ return appIdData->hsession->xffAddr;
+ return nullptr;
+}
+
+char* getTlsHost(AppIdData* appIdData)
+{
+ if (appIdData && appIdData->tsession)
+ return appIdData->tsession->tls_host;
+ return nullptr;
+}
+
+AppId getPorServiceAppId(AppIdData* appIdData)
+{
+ if (appIdData)
+ return appIdData->portServiceAppId;
+ return APP_ID_NONE;
+}
+
+// FIXIT used to be sfaddr_t
+sfip_t* getServiceIp(AppIdData* appIdData)
+{
+ if (appIdData)
+ return &appIdData->service_ip;
+ return nullptr;
+}
+
+// FIXIT used to be sfaddr_t
+sfip_t* getInitiatorIp(AppIdData* appIdData)
+{
+ return appIdData ? &appIdData->common.initiator_ip : nullptr;
+}
+
+DhcpFPData* getDhcpFpData(AppIdData* appIdData)
+{
+ if (appIdData && getAppIdFlag(appIdData, APPID_SESSION_HAS_DHCP_FP))
+ return static_cast<DhcpFPData*>(
+ AppIdFlowdataRemove(appIdData, APPID_SESSION_DATA_DHCP_FP_DATA));
+
+ return nullptr;
+}
+
+void freeDhcpFpData(AppIdData* appIdData, DhcpFPData* data)
+{
+ if (appIdData)
+ {
+ clearAppIdFlag(appIdData, APPID_SESSION_HAS_DHCP_FP);
+ AppIdFreeDhcpData(data);
+ }
+}
+
+DHCPInfo* getDhcpInfo(AppIdData* appIdData)
+{
+ if (appIdData && getAppIdFlag(appIdData, APPID_SESSION_HAS_DHCP_INFO))
+ return static_cast<DHCPInfo*>(
+ AppIdFlowdataRemove(appIdData, APPID_SESSION_DATA_DHCP_INFO));
+
+ return nullptr;
+}
+
+void freeDhcpInfo(AppIdData* appIdData, DHCPInfo* data)
+{
+ if (appIdData)
+ {
+ clearAppIdFlag(appIdData, APPID_SESSION_HAS_DHCP_INFO);
+ AppIdFreeDhcpInfo(data);
+ }
+}
+
+FpSMBData* getSmbFpData(AppIdData* appIdData)
+{
+ if (appIdData && getAppIdFlag(appIdData, APPID_SESSION_HAS_SMB_INFO))
+ return static_cast<FpSMBData*>(
+ AppIdFlowdataRemove(appIdData, APPID_SESSION_DATA_SMB_DATA));
+
+ return nullptr;
+}
+
+void freeSmbFpData(AppIdData* appIdData, FpSMBData* data)
+{
+ if (appIdData)
+ {
+ clearAppIdFlag(appIdData, APPID_SESSION_HAS_SMB_INFO);
+ AppIdFreeSMBData(data);
+ }
+}
+
+char* getNetbiosName(AppIdData* appIdData)
+{
+ if (appIdData)
+ {
+ char* netbiosName = appIdData->netbios_name;
+ appIdData->netbios_name = nullptr; //transfer ownership to caller.
+ return netbiosName;
+ }
+ return nullptr;
+}
+
+#define APPID_HA_FLAGS_APP (1<<0)
+#define APPID_HA_FLAGS_TP_DONE (1<<1)
+#define APPID_HA_FLAGS_SVC_DONE (1<<2)
+#define APPID_HA_FLAGS_HTTP (1<<3)
+
+uint32_t produceHAState(void* lwssn, uint8_t* buf)
+{
+ AppIdSessionHA* appHA = (AppIdSessionHA*)buf;
+ AppIdData* appIdData = (AppIdData*)(((Flow*)lwssn)->get_application_data(AppIdData::flow_id));
+
+ // FIXIT - getFlowType should be a class member
+ if (appIdData && getFlowType(appIdData) != APPID_FLOW_TYPE_NORMAL)
+ appIdData = nullptr;
+ 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->appId, 0, sizeof(appHA->appId));
+ }
+ return sizeof(*appHA);
+}
+
+// FIXIT last arg used to be sfaddr_t
+uint32_t consumeHAState(void* lwssn, const uint8_t* buf, uint8_t, IpProtocol proto,
+ sfip_t* ip)
+{
+ AppIdSessionHA* appHA = (AppIdSessionHA*)buf;
+ if (appHA->flags & APPID_HA_FLAGS_APP)
+ {
+ AppIdData* appIdData = (AppIdData*)(((Flow*)lwssn)->get_application_data(
+ AppIdData::flow_id));
+
+ if (!appIdData)
+ {
+ appIdData = appSharedDataAlloc(proto, ip);
+ ((Flow*)lwssn)->set_application_data(appIdData);
+ 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(AppIdData* appIdData, uint8_t* query_len)
+{
+ if (appIdData && appIdData->dsession)
+ {
+ if (query_len)
+ {
+ if (appIdData->dsession->host)
+ *query_len = appIdData->dsession->host_len;
+
+ else
+ *query_len = 0;
+ }
+
+ return appIdData->dsession->host;
+ }
+ if (query_len)
+ *query_len = 0;
+ return nullptr;
+}
+
+uint16_t getDNSQueryoffset(AppIdData* appIdData)
+{
+ if (appIdData && appIdData->dsession)
+ return appIdData->dsession->host_offset;
+ return 0;
+}
+
+uint16_t getDNSRecordType(AppIdData* appIdData)
+{
+ if (appIdData && appIdData->dsession)
+ return appIdData->dsession->record_type;
+ return 0;
+}
+
+uint8_t getDNSResponseType(AppIdData* appIdData)
+{
+ if (appIdData && appIdData->dsession)
+ return appIdData->dsession->response_type;
+ return 0;
+}
+
+uint32_t getDNSTTL(AppIdData* appIdData)
+{
+ if (appIdData && appIdData->dsession)
+ return appIdData->dsession->ttl;
+ return 0;
+}
+
+static AppIdApi appIdDispatchTable =
+{
+ appGetAppName,
+ appGetAppId,
+
+ getServiceAppId,
+ getPorServiceAppId,
+ getOnlyServiceAppId,
+ getMiscAppId,
+ getClientAppId,
+ getPayloadAppId,
+ getReferredAppId,
+ getFwServiceAppId,
+ getFwMiscAppId,
+ getFwClientAppId,
+ getFwPayloadAppId,
+ getFwReferredAppId,
+
+ 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,
+
+ getDNSQuery,
+ getDNSQueryoffset,
+ getDNSRecordType,
+ getDNSResponseType,
+ getDNSTTL,
+
+ getHttpNewField,
+ freeHttpNewField,
+};
+
+void appIdApiInit(struct AppIdApi* api)
+{
+ *api = appIdDispatchTable;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// appid_api.h author Sourcefire Inc.
+
+#ifndef APPID_API_H
+#define APPID_API_H
+
+#include <cstdint>
+
+enum class IpProtocol : uint8_t;
+
+#define APPID_SESSION_RESPONDER_MONITORED (1ULL << 0)
+#define APPID_SESSION_INITIATOR_MONITORED (1ULL << 1)
+#define APPID_SESSION_SPECIAL_MONITORED (1ULL << 2)
+#define APPID_SESSION_INITIATOR_SEEN (1ULL << 3)
+#define APPID_SESSION_RESPONDER_SEEN (1ULL << 4)
+#define APPID_SESSION_DISCOVER_USER (1ULL << 5)
+#define APPID_SESSION_HAS_DHCP_FP (1ULL << 6)
+#define APPID_SESSION_HAS_DHCP_INFO (1ULL << 7)
+#define APPID_SESSION_HAS_SMB_INFO (1ULL << 8)
+#define APPID_SESSION_MID (1ULL << 9)
+#define APPID_SESSION_OOO (1ULL << 10)
+#define APPID_SESSION_SYN_RST (1ULL << 11)
+
+ /**Service missed the first UDP packet in a flow. This causes detectors to see traffic in reverse direction.
+ * Detectors should set this flag by verifying that packet from initiator is indeed a packet from responder.
+ * Setting this flag without this check will cause RNA to not try other detectors in some cases (see bug 77551).*/
+#define APPID_SESSION_UDP_REVERSED (1ULL << 12)
+#define APPID_SESSION_HTTP_SESSION (1ULL << 13)
+
+ /**Service protocol was detected */
+#define APPID_SESSION_SERVICE_DETECTED (1ULL << 14)
+
+ /**Finsihed with client app detection */
+#define APPID_SESSION_CLIENT_DETECTED (1ULL << 15)
+ /**Flow is a data connection not a service */
+#define APPID_SESSION_NOT_A_SERVICE (1ULL << 16)
+
+#define APPID_SESSION_DECRYPTED (1ULL << 17)
+#define APPID_SESSION_SERVICE_DELETED (1ULL << 18)
+
+ //The following attributes are references only with appId
+ /**Continue calling the routine after the service has been identified. */
+#define APPID_SESSION_CONTINUE (1ULL << 19)
+ /**Call service detection even if the host does not exist */
+#define APPID_SESSION_IGNORE_HOST (1ULL << 20)
+ /**Service protocol had incompatible client data */
+#define APPID_SESSION_INCOMPATIBLE (1ULL << 21)
+ /**we are ready to see out of network Server packets */
+#define APPID_SESSION_CLIENT_GETS_SERVER_PACKETS (1ULL << 22)
+
+#define APPID_SESSION_DISCOVER_APP (1ULL << 23)
+
+#define APPID_SESSION_PORT_SERVICE_DONE (1ULL << 24)
+#define APPID_SESSION_ADDITIONAL_PACKET (1ULL << 25)
+#define APPID_SESSION_RESPONDER_CHECKED (1ULL << 26)
+#define APPID_SESSION_INITIATOR_CHECKED (1ULL << 27)
+#define APPID_SESSION_SSL_SESSION (1ULL << 28)
+#define APPID_SESSION_LOGIN_SUCCEEDED (1ULL << 29)
+
+#define APPID_SESSION_SPDY_SESSION (1ULL << 30)
+#define APPID_SESSION_ENCRYPTED (1ULL << 31)
+
+#define APPID_SESSION_APP_REINSPECT (1ULL << 32)
+#define APPID_SESSION_RESPONSE_CODE_CHECKED (1ULL << 33)
+#define APPID_SESSION_REXEC_STDERR (1ULL << 34)
+#define APPID_SESSION_CHP_INSPECTING (1ULL << 35)
+#define APPID_SESSION_STICKY_SERVICE (1ULL << 36)
+#define APPID_SESSION_APP_REINSPECT_SSL (1ULL << 37)
+
+#define APPID_SESSION_NO_TPI (1ULL << 38)
+#define APPID_SESSION_IGNORE_FLOW (1ULL << 39)
+#define APPID_SESSION_IGNORE_FLOW_LOGGED (1ULL << 40)
+
+#define APPID_SESSION_OOO_LOGGED (1ULL << 41)
+#define APPID_SESSION_TPI_OOO_LOGGED (1ULL << 42)
+
+#define APPID_SESSION_EXPECTED_EVALUATE (1ULL << 43)
+
+#define APPID_SESSION_IGNORE_ID_FLAGS (APPID_SESSION_IGNORE_FLOW | \
+ APPID_SESSION_NOT_A_SERVICE | \
+ APPID_SESSION_NO_TPI | \
+ APPID_SESSION_SERVICE_DETECTED | \
+ APPID_SESSION_PORT_SERVICE_DONE)
+
+
+using AppId = int32_t;
+class AppIdData;
+
+enum APPID_FLOW_TYPE
+{
+ APPID_FLOW_TYPE_IGNORE,
+ APPID_FLOW_TYPE_NORMAL,
+ APPID_FLOW_TYPE_TMP
+};
+
+struct RNAServiceSubtype
+{
+ RNAServiceSubtype *next;
+ const char *service;
+ const char *vendor;
+ const char *version;
+};
+
+#define DHCP_OP55_MAX_SIZE 64
+#define DHCP_OP60_MAX_SIZE 64
+
+struct DhcpFPData
+{
+ DhcpFPData *next;
+ unsigned op55_len;
+ unsigned op60_len;
+ uint8_t op55[DHCP_OP55_MAX_SIZE];
+ uint8_t op60[DHCP_OP60_MAX_SIZE];
+ // FIXIT-L J should be using eth address type here
+ uint8_t mac[6];
+} ;
+
+// FIXIT-L J inconsistently named structs (DHCPInfo vs DhcpFPData, note the "DHCP")
+struct DHCPInfo
+{
+ DHCPInfo *next;
+ uint32_t ipAddr;
+ // FIXIT-L J should be using eth address type here
+ // FIXIT-L J inconsistently named fields (macAddr here, mac in DhcpFPData)
+ uint8_t macAddr[6];
+ uint32_t subnetmask;
+ uint32_t leaseSecs;
+ uint32_t router;
+};
+
+struct FpSMBData
+{
+ FpSMBData *next;
+ unsigned major;
+ unsigned minor;
+ uint32_t flags;
+};
+
+//maximum number of appIds replicated for a flow/session
+#define APPID_HA_SESSION_APP_NUM_MAX 8
+
+struct AppIdSessionHA
+{
+ uint16_t flags;
+ AppId appId[APPID_HA_SESSION_APP_NUM_MAX];
+};
+
+enum SEARCH_SUPPORT_TYPE
+{
+ // FIXIT-L J enums are inconsistently named
+ NOT_A_SEARCH_ENGINE,
+ SUPPORTED_SEARCH_ENGINE,
+ UNSUPPORTED_SEARCH_ENGINE,
+ SEARCH_SUPPORT_TYPE_UNKNOWN,
+};
+
+// FIXIT-M J probable duplication from nhttp
+enum HTTP_FIELD_ID
+{
+ REQ_AGENT_FID = 0,
+ REQ_HOST_FID = 1,
+ REQ_REFERER_FID = 2,
+ REQ_URI_FID = 3,
+ REQ_COOKIE_FID = 4,
+ REQ_BODY_FID = 5,
+ RSP_CONTENT_TYPE_FID = 6,
+ RSP_LOCATION_FID = 7,
+ RSP_BODY_FID = 8,
+ HTTP_FIELD_MAX = RSP_BODY_FID
+};
+
+// -----------------------------------------------------------------------------
+// AppId API
+// -----------------------------------------------------------------------------
+
+struct sfip_t;
+// FIXIT-H J should be class with static methods
+struct AppIdApi
+{
+ const char * (*geApplicationName)(int32_t appId);
+ AppId (*geApplicationId)(const char* appName);
+
+ AppId (*geServiceAppId)(AppIdData*);
+ AppId (*getPorServiceAppId)(AppIdData*);
+ AppId (*getOnlyServiceAppId)(AppIdData*);
+ AppId (*getMiscAppId)(AppIdData*);
+ AppId (*getClientAppId)(AppIdData*);
+ AppId (*getPayloadAppId)(AppIdData*);
+ AppId (*getReferredAppId)(AppIdData*);
+ AppId (*getFwServiceAppId)(AppIdData*);
+ AppId (*getFwMiscAppId)(AppIdData*);
+ AppId (*getFwClientAppId)(AppIdData*);
+ AppId (*getFwPayloadAppId)(AppIdData*);
+ AppId (*getFwReferredAppId)(AppIdData*);
+
+ bool (*isSessionSslDecrypted)(AppIdData*);
+ bool (*isAppIdInspectingSession)(AppIdData*);
+ bool (*isAppIdAvailable)(AppIdData*);
+
+ char* (*getUserName)(AppIdData*, AppId* service, bool* isLoginSuccessful);
+ char* (*geClientVersion)(AppIdData*);
+
+ uint64_t (*getAppIdSessionAttribute)(AppIdData*, uint64_t flag);
+
+ APPID_FLOW_TYPE (*getFlowType)(AppIdData*);
+ void (*geServiceInfo)(AppIdData*, char **serviceVendor, char** serviceVersion, RNAServiceSubtype** subtype);
+ short (*geServicePort)(AppIdData*);
+ sfip_t* (*geServiceIp)(AppIdData*);
+ sfip_t* (*getInitiatorIp)(AppIdData*);
+
+ char* (*getHttpUserAgent)(AppIdData*);
+ char* (*getHttpHost)(AppIdData*);
+ char* (*getHttpUrl)(AppIdData*);
+ char* (*getHttpReferer)(AppIdData*);
+ char* (*getHttpNewUrl)(AppIdData*);
+ char* (*getHttpUri)(AppIdData*);
+ char* (*getHttpResponseCode)(AppIdData*);
+ char* (*getHttpCookie)(AppIdData*);
+ char* (*getHttpNewCookie)(AppIdData*);
+ char* (*getHttpContentType)(AppIdData*);
+ char* (*getHttpLocation)(AppIdData*);
+ char* (*getHttpBody)(AppIdData*);
+ char* (*getHttpReqBody)(AppIdData*);
+ uint16_t (*getHttpUriOffset)(AppIdData*);
+ uint16_t (*getHttpUriEndOffset)(AppIdData*);
+ uint16_t (*getHttpCookieOffset)(AppIdData*);
+ uint16_t (*getHttpCookieEndOffset)(AppIdData*);
+ SEARCH_SUPPORT_TYPE (*getHttpSearch)(AppIdData*);
+ sfip_t* (*getHttpXffAddr)(AppIdData*);
+
+ char* (*getTlsHost)(AppIdData*);
+
+ DhcpFPData* (*getDhcpFpData)(AppIdData*);
+ void (*freeDhcpFpData)(AppIdData*, DhcpFPData*);
+ DHCPInfo* (*getDhcpInfo)(AppIdData*);
+ void (*freeDhcpInfo)(AppIdData*, DHCPInfo*);
+ FpSMBData* (*getSmbFpData)(AppIdData*);
+ void (*freeSmbFpData)(AppIdData*, FpSMBData*);
+ char* (*getNetbiosName)(AppIdData*);
+ uint32_t (*produceHAState)(void* lwssn, uint8_t* buf);
+ uint32_t (*consumeHAState)(void* lwssn, const uint8_t* buf, uint8_t length, IpProtocol proto, sfip_t* ip);
+ AppIdData* (*getAppIdData)(void* lwssn);
+
+ char* (*getDNSQuery)(AppIdData*, uint8_t* query_len);
+ uint16_t (*getDNSQueryoffset)(AppIdData*);
+ uint16_t (*getDNSRecordType)(AppIdData*);
+ uint8_t (*getDNSResponseType)(AppIdData*);
+ uint32_t (*getDNSTTL)(AppIdData*);
+ char* (*getHttpNewField)(AppIdData*, HTTP_FIELD_ID);
+ void (*freeHttpNewField)(AppIdData*, HTTP_FIELD_ID);
+};
+
+// For access when including header
+extern AppIdApi appIdApi;
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// appid_config.cc author Sourcefire Inc.
+#include <cstring>
+#include <glob.h>
+
+#include "appid_config.h"
+#include "appid_stats.h"
+#include "app_info_table.h"
+#include "application_ids.h"
+#include "app_forecast.h"
+#include "fw_appid.h"
+#include "host_port_app_cache.h"
+#include "thirdparty_appid_utils.h"
+#include "client_plugins/client_app_base.h"
+#include "service_plugins/service_base.h"
+#include "service_plugins/service_ssl.h"
+#include "detector_plugins/detector_base.h"
+#include "detector_plugins/detector_http.h"
+#include "detector_plugins/detector_dns.h"
+
+#include "util/network_set.h"
+#include "util/ip_funcs.h"
+#include "util/common_util.h"
+#include "util/sfutil.h"
+
+#include "lua_detector_api.h"
+#include "lua_detector_module.h"
+
+#include "main/snort_debug.h"
+#include "log/messages.h"
+#include "utils/util.h"
+
+#define ODP_PORT_DETECTORS "odp/port/*"
+#define CUSTOM_PORT_DETECTORS "custom/port/*"
+
+/*static const char * const MODULE_NAME = "AppMatcher"; */
+#define MAX_DISPLAY_SIZE 65536
+
+// FIXIT - M this global needs to go asap... just here now to compile while doing some major config
+// refactoring
+AppIdConfig* pAppidActiveConfig = nullptr;
+
+unsigned appIdPolicyId;
+uint32_t app_id_netmasks[33];
+
+struct PortList
+{
+ PortList* next;
+ uint16_t port;
+};
+
+AppIdModuleConfig::~AppIdModuleConfig()
+{
+ snort_free((void*)conf_file);
+ snort_free((void*)app_stats_filename);
+ snort_free((void*)app_detector_dir);
+ snort_free((void*)thirdparty_appid_dir);
+}
+
+void AppIdConfig::add_generic_config_element(const char* name, void* pData)
+{
+ AppidGenericConfigItem* pConfigItem;
+
+ pConfigItem = (AppidGenericConfigItem*)snort_calloc(sizeof(AppidGenericConfigItem));
+ pConfigItem->name = snort_strdup(name);
+ pConfigItem->pData = pData;
+ sflist_add_tail(&genericConfigList, pConfigItem);
+}
+
+void* AppIdConfig::find_generic_config_element(const char* name)
+{
+ AppidGenericConfigItem* pConfigItem;
+ SF_LNODE* next;
+
+ // Search a module's configuration by its name
+ for (pConfigItem = (AppidGenericConfigItem*)sflist_first(
+ (SF_LIST*)&genericConfigList, &next);
+ pConfigItem != nullptr;
+ pConfigItem = (AppidGenericConfigItem*)sflist_next(&next))
+ {
+ if (strcmp(pConfigItem->name, name) == 0)
+ {
+ return pConfigItem->pData;
+ }
+ }
+
+ return nullptr;
+}
+
+void AppIdConfig::remove_generic_config_element(const char* name)
+{
+ SF_LNODE* iter;
+ AppidGenericConfigItem* cfg;
+
+ // Search a module's configuration by its name
+ for (cfg = (AppidGenericConfigItem*)sflist_first(&genericConfigList, &iter);
+ cfg != nullptr;
+ cfg = (AppidGenericConfigItem*)sflist_next(&iter))
+ {
+ if (strcmp(cfg->name, name) == 0)
+ {
+ snort_free(cfg->name);
+ snort_free(cfg);
+ sflist_remove_node(&genericConfigList, iter);
+ break;
+ }
+ }
+}
+
+void AppIdConfig::read_port_detectors(const char* files)
+{
+ int rval;
+ glob_t globs;
+ char pattern[PATH_MAX];
+ uint32_t n;
+
+ snprintf(pattern, sizeof(pattern), "%s/%s", mod_config->app_detector_dir, files);
+
+ memset(&globs, 0, sizeof(globs));
+ rval = glob(pattern, 0, nullptr, &globs);
+ if (rval != 0 && rval != GLOB_NOMATCH)
+ {
+ ErrorMessage("Unable to read directory '%s'\n",pattern);
+ return;
+ }
+
+ for (n = 0; n < globs.gl_pathc; n++)
+ {
+ FILE* file;
+ unsigned proto = 0;
+ AppId appId = APP_ID_NONE;
+ char line[1024];
+ PortList* port = nullptr;
+ PortList* tmp_port;
+
+ if ((file = fopen(globs.gl_pathv[n], "r")) == nullptr)
+ {
+ ErrorMessage("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 = nullptr;
+ char* ptr;
+ unsigned long tmp;
+
+ for (ptr = strtok_r(value, ",", &context); ptr; ptr = strtok_r(nullptr, ",",
+ &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)
+ {
+ ErrorMessage("Invalid port, '%s', in lua detector '%s'\n",ptr,
+ globs.gl_pathv[n]);
+ goto next;
+ }
+ tmp_port = (PortList*)snort_calloc(sizeof(PortList));
+ 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
+ {
+ ErrorMessage("Invalid protocol, '%s', in port service '%s'\n",value,
+ globs.gl_pathv[n]);
+ goto next;
+ }
+ }
+ else if (strcasecmp(key, "appId") == 0)
+ {
+ appId = (AppId)strtoul(value, &p, 10);
+ if (!*value || *p || appId <= APP_ID_NONE)
+ {
+ ErrorMessage("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)
+ tcp_port_only[tmp_port->port] = appId;
+ if (proto & 2)
+ udp_port_only[tmp_port->port] = appId;
+
+ snort_free(tmp_port);
+ appInfoSetActive(appId, true);
+ }
+ appInfoSetActive(appId, true);
+ }
+ else
+ ErrorMessage("Missing parameter(s) in port service '%s'\n",globs.gl_pathv[n]);
+
+next:;
+ while ((tmp_port = port))
+ {
+ port = tmp_port->next;
+ snort_free(tmp_port);
+ }
+ fclose(file);
+ }
+
+ globfree(&globs);
+}
+
+void AppIdConfig::configure_analysis_networks(char* toklist[], uint32_t flag)
+{
+ int zone;
+ NetworkSet* my_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)
+ {
+ ErrorMessage("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));
+ LogMessage("Adding %s-%s (0x%08X) with zone %d\n", min_ip, max_ip,
+ ias6->addr_flags, zone);
+ if (zone >= 0)
+ {
+ if (!(my_net_list = net_list_by_zone[zone]))
+ {
+ if (NetworkSet_New(&my_net_list))
+ ErrorMessage("%s", "Failed to create a network set");
+ else
+ {
+ my_net_list->next = net_list_list;
+ net_list_list = my_net_list;
+ }
+ net_list_by_zone[zone] = my_net_list;
+ }
+ }
+ else
+ my_net_list = net_list;
+ if (my_net_list && NetworkSet_AddCidrBlock6Ex(my_net_list, &ias6->range_min,
+ ias6->netmask,
+ ias6->addr_flags & IPFUNCS_EXCEPT_IP, 0,
+ ias6->addr_flags & (~IPFUNCS_EXCEPT_IP)))
+ {
+ ErrorMessage(
+ "Failed to add an IP address set to the list of monitored networks");
+ }
+ snort_free(ias6);
+ }
+ else
+ ErrorMessage("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)
+ {
+ ErrorMessage("Invalid Analyze: %s '%s'", toklist[0], toklist[1]);
+ zone = -1;
+ }
+ else
+ zone = (int)tmp;
+ }
+ else
+ zone = -1;
+ ias->addr_flags |= flag;
+ LogMessage("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 (!(my_net_list = net_list_by_zone[zone]))
+ {
+ if (NetworkSet_New(&my_net_list))
+ ErrorMessage("%s", "Failed to create a network set");
+ else
+ {
+ my_net_list->next = net_list_list;
+ net_list_list = my_net_list;
+ }
+ net_list_by_zone[zone] = my_net_list;
+ }
+ }
+ else
+ my_net_list = net_list;
+ if (my_net_list && NetworkSet_AddCidrBlockEx(my_net_list, ias->range_min,
+ ias->netmask,
+ ias->addr_flags & IPFUNCS_EXCEPT_IP, 0,
+ ias->addr_flags & (~IPFUNCS_EXCEPT_IP)))
+ {
+ ErrorMessage(
+ "Failed to add an IP address set to the list of monitored networks");
+ }
+ snort_free(ias);
+ }
+ else
+ ErrorMessage("Invalid analysis parameter: %s", toklist[0]);
+ }
+ }
+}
+
+int AppIdConfig::add_port_exclusion(SF_LIST* port_exclusions[], const ip::snort_in6_addr* ip,
+ const ip::snort_in6_addr* netmask, int family, uint16_t port)
+{
+ PortExclusion* port_ex;
+ SF_LIST* pe_list;
+
+ port_ex = (PortExclusion*)snort_calloc(sizeof(PortExclusion));
+ port_ex->ip = *ip;
+ if (family == AF_INET)
+ {
+ port_ex->netmask.u6_addr32[0] = port_ex->netmask.u6_addr32[1] =
+ port_ex->netmask.u6_addr32[2] = ~0;
+ port_ex->netmask.u6_addr32[3] = netmask->u6_addr32[3];
+ }
+ else
+ port_ex->netmask = *netmask;
+
+ if ((pe_list = port_exclusions[port]) == nullptr)
+ {
+ pe_list = port_exclusions[port] = sflist_new();
+ if (pe_list == nullptr)
+ {
+ snort_free(port_ex);
+ ErrorMessage("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) )
+ {
+ snort_free(port_ex);
+ ErrorMessage("Config: Failed to add an port exclusion to the list");
+ return -1;
+ }
+ return 0;
+}
+
+void AppIdConfig::process_port_exclusion(char* toklist[])
+{
+ int i = 1;
+ char* p;
+ RNAIpAddrSet* ias;
+ RNAIpv6AddrSet* ias6;
+ SF_LIST** port_exclusions;
+ IpProtocol proto;
+ unsigned long dir;
+ unsigned long port;
+ ip::snort_in6_addr ip;
+ ip::snort_in6_addr netmask;
+ int family;
+
+ if (!toklist[i])
+ {
+ ErrorMessage("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
+ {
+ ErrorMessage("Config: Invalid port exclusion direction specified");
+ return;
+ }
+
+ i++;
+ if (!toklist[i])
+ {
+ ErrorMessage("Config: Port exclusion protocol omitted");
+ return;
+ }
+
+ if (strcasecmp(toklist[i], "tcp") == 0)
+ proto = IpProtocol::TCP;
+ else if (strcasecmp(toklist[i], "udp") == 0)
+ proto = IpProtocol::UDP;
+ else
+ {
+ ErrorMessage("Config: Invalid port exclusion protocol specified");
+ return;
+ }
+
+ i++;
+ if (!toklist[i])
+ {
+ ErrorMessage("Config: Port exclusion port omitted");
+ return;
+ }
+
+ port = strtoul(toklist[i], &p, 10);
+ if (!*toklist[i] || *p || port >= APP_ID_PORT_ARRAY_SIZE)
+ {
+ ErrorMessage("Config: Invalid port exclusion port specified");
+ return;
+ }
+
+ i++;
+ if (!toklist[i])
+ {
+ ErrorMessage("Config: Port exclusion address omitted");
+ return;
+ }
+
+ if (strchr(toklist[i], ':'))
+ {
+ ias6 = ParseIpv6Cidr(toklist[i]);
+ if (!ias6 || ias6->addr_flags)
+ {
+ if (ias6)
+ snort_free(ias6);
+ ErrorMessage("Config: Invalid port exclusion address specified");
+ return;
+ }
+ NSIPv6AddrHtoNConv(&ias6->range_min, &ip);
+ NSIPv6AddrHtoNConv(&ias6->netmask_mask, &netmask);
+ family = AF_INET6;
+ snort_free(ias6);
+ }
+ else
+ {
+ ias = ParseIpCidr(toklist[i], app_id_netmasks);
+ if (!ias || ias->addr_flags)
+ {
+ if (ias)
+ snort_free(ias);
+ ErrorMessage("Config: Invalid port exclusion address specified");
+ return;
+ }
+ family = AF_INET;
+ copyIpv4ToIpv6Network(&ip, htonl(ias->range_min));
+ copyIpv4ToIpv6Network(&netmask, htonl(ias->netmask_mask));
+ snort_free(ias);
+ }
+
+ if (dir & 1)
+ {
+ if (proto == IpProtocol::TCP)
+ port_exclusions = tcp_port_exclusions_src;
+ else
+ port_exclusions = udp_port_exclusions_src;
+ add_port_exclusion(port_exclusions, &ip, &netmask, family, (uint16_t)port);
+ }
+ if (dir & 2)
+ {
+ if (proto == IpProtocol::TCP)
+ port_exclusions = tcp_port_exclusions_dst;
+ else
+ port_exclusions = udp_port_exclusions_dst;
+ add_port_exclusion(port_exclusions, &ip, &netmask, family, (uint16_t)port);
+ }
+}
+
+void AppIdConfig::process_config_directive(char* toklist[], 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"))
+ {
+ configure_analysis_networks(&toklist[i], IPFUNCS_HOSTS_IP | IPFUNCS_APPLICATION);
+ }
+ else if (!strcasecmp(curtok, "AnalyzeHost"))
+ {
+ configure_analysis_networks(&toklist[i], IPFUNCS_HOSTS_IP | IPFUNCS_APPLICATION);
+ }
+ else if (!strcasecmp(curtok, "AnalyzeUser"))
+ {
+ configure_analysis_networks(&toklist[i], IPFUNCS_USER_IP | IPFUNCS_APPLICATION);
+ }
+ else if (!strcasecmp(curtok, "AnalyzeHostUser"))
+ {
+ configure_analysis_networks(&toklist[i],
+ IPFUNCS_HOSTS_IP | IPFUNCS_USER_IP | IPFUNCS_APPLICATION);
+ }
+ else if (!strcasecmp(curtok, "AnalyzeApplication"))
+ {
+ configure_analysis_networks(&toklist[i], IPFUNCS_APPLICATION);
+ }
+}
+
+int AppIdConfig::load_analysis_config(const char* config_file, int reload, int instance_id)
+{
+ FILE* fp;
+ char linebuffer[MAX_LINE];
+ char* cptr;
+ char* toklist[MAX_TOKS];
+ int num_toks;
+ unsigned line = 0;
+ NetworkSet* my_net_list;
+
+ if (NetworkSet_New(&net_list))
+ {
+ FatalError("Failed to allocate a network set");
+ exit(1);
+ }
+ net_list_list = net_list;
+
+ if (!config_file || (!config_file[0]))
+ {
+ char addrString[sizeof("0.0.0.0/0")];
+ LogMessage("Defaulting to monitoring all Snort traffic for AppID.\n");
+ toklist[1] = nullptr;
+ toklist[0] = addrString;
+ strcpy(addrString,"0.0.0.0/0");
+ configure_analysis_networks(toklist, IPFUNCS_HOSTS_IP | IPFUNCS_USER_IP |
+ IPFUNCS_APPLICATION);
+ strcpy(addrString,"::/0");
+ configure_analysis_networks(toklist, IPFUNCS_HOSTS_IP | IPFUNCS_USER_IP |
+ IPFUNCS_APPLICATION);
+ toklist[0] = nullptr;
+ }
+ else
+ {
+ DebugFormat(DEBUG_APPID, "Loading configuration file: %s", config_file);
+
+ if (!(fp = fopen(config_file, "r")))
+ {
+ ErrorMessage("Unable to open %s", config_file);
+ return -1;
+ }
+
+ while (fgets(linebuffer, MAX_LINE, fp) != nullptr)
+ {
+ 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);
+ ErrorMessage("Invalid configuration file line %u", line);
+ return -1;
+ }
+ if (!(strcasecmp(toklist[0], "config")))
+ {
+ process_config_directive(toklist, reload);
+ }
+ else if (!(strcasecmp(toklist[0], "portexclusion")))
+ {
+ process_port_exclusion(toklist);
+ }
+ }
+ }
+
+ fclose(fp);
+ }
+
+ if (instance_id)
+ {
+ char* instance_toklist[2];
+ char addrString[sizeof("0.0.0.0/0")];
+ LogMessage("Defaulting to monitoring all Snort traffic for AppID.\n");
+ instance_toklist[0] = addrString;
+ instance_toklist[1] = nullptr;
+ strcpy(addrString,"0.0.0.0/0");
+ configure_analysis_networks(instance_toklist, IPFUNCS_APPLICATION);
+ strcpy(addrString,"::/0");
+ configure_analysis_networks(instance_toklist, IPFUNCS_APPLICATION);
+ }
+
+ for (my_net_list = net_list_list; my_net_list; my_net_list = net_list->next)
+ {
+ if (my_net_list != net_list)
+ {
+ if (NetworkSet_AddSet(my_net_list, net_list))
+ ErrorMessage("Failed to add any network list to a zone network list");
+ }
+ }
+ net_list_count = 0;
+ for (my_net_list = net_list_list; my_net_list; my_net_list = net_list->next)
+ {
+ if (NetworkSet_Reduce(my_net_list))
+ ErrorMessage("Failed to reduce the IP address sets");
+ net_list_count += NetworkSet_CountEx(my_net_list) + NetworkSet_Count6Ex(my_net_list);
+ }
+
+ return 0;
+}
+
+void AppIdConfig::load_modules(uint32_t instance_id)
+{
+ if (LoadServiceModules(nullptr, instance_id, this))
+ exit(-1);
+
+ if (LoadClientAppModules(this))
+ exit(-1);
+
+ if (LoadDetectorModules(nullptr))
+ exit(-1);
+}
+
+void AppIdConfig::finalize_pattern_modules()
+{
+ unsigned int i;
+ ServicePatternData* curr;
+ ServicePatternData* lists[] = { serviceConfig.tcp_pattern_data,
+ serviceConfig.udp_pattern_data };
+ for (i = 0; i < (sizeof(lists) / sizeof(*lists)); i++)
+ {
+ curr = lists[i];
+ while (curr != nullptr)
+ {
+ if (curr->svc != nullptr)
+ {
+ 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;
+ }
+ }
+}
+
+int AppIdConfig::init_AF_indicators()
+{
+ if (!(AF_indicators = sfxhash_new(1024, sizeof(AppId), sizeof(AFElement), 0,
+ 0, nullptr, nullptr, 0)))
+ {
+ ErrorMessage("Config: failed to allocate memory for an sfxhash.");
+ return 0;
+ }
+ else
+ return 1;
+}
+
+int AppIdConfig::init_AF_actives()
+{
+ if (!(AF_actives = sfxhash_new(1024, sizeof(AFActKey), sizeof(AFActVal),
+ (sizeof(SFXHASH_NODE)*2048), 1, nullptr, nullptr, 1)))
+ {
+ ErrorMessage("Config: failed to allocate memory for an sfxhash.");
+ return 0;
+ }
+ else
+ return 1;
+}
+
+static int genericDataFree(void* /* key */, void* data)
+{
+ if (data)
+ snort_free(data);
+ return 0;
+}
+
+int AppIdConfig::init_CHP_glossary()
+{
+ if (!(CHP_glossary = sfxhash_new(1024, sizeof(AppId), 0, 0, 0, nullptr, &genericDataFree, 0)))
+ {
+ ErrorMessage("Config: failed to allocate memory for an sfxhash.");
+ return 0;
+ }
+ else
+ return 1;
+}
+
+void AppIdConfig::set_safe_search_enforcement(int enabled)
+{
+ DEBUG_WRAP(DebugFormat(DEBUG_APPID, " Safe Search Enforcement enabled = %d.\n",enabled); );
+ mod_config->disable_safe_search = enabled ? 0 : 1;
+}
+
+bool AppIdConfig::init_appid( )
+{
+ fwAppIdInit();
+
+ if (config_state == RNA_FW_CONFIG_STATE_UNINIT)
+ {
+ appIdPolicyId = 53;
+ // FIXIT - active config must be per Inspector instance...not this global...
+ pAppidActiveConfig = this;
+ config_state = RNA_FW_CONFIG_STATE_PENDING;
+
+ InitNetmasks(app_id_netmasks);
+ sflist_init(&pAppidActiveConfig->client_app_args);
+ load_analysis_config(mod_config->conf_file, 0, mod_config->instance_id);
+ if (!init_CHP_glossary( ))
+ return false;
+ if (!init_AF_indicators( ))
+ return false;
+ if (!init_AF_actives( ))
+ return false;
+ LuaDetectorModuleManager::luaModuleInit();
+ appInfoTableInit(mod_config->app_detector_dir, pAppidActiveConfig);
+ read_port_detectors(ODP_PORT_DETECTORS);
+ read_port_detectors(CUSTOM_PORT_DETECTORS);
+ load_modules(mod_config->instance_id);
+ hostPortAppCacheInit(pAppidActiveConfig);
+ lengthAppCacheInit(pAppidActiveConfig);
+
+ LuaDetectorModuleManager::LoadLuaModules(pAppidActiveConfig);
+ ClientAppInit(pAppidActiveConfig);
+ ServiceInit(pAppidActiveConfig);
+ LuaDetectorModuleManager::FinalizeLuaModules(pAppidActiveConfig);
+
+ finalize_pattern_modules();
+
+ http_detector_finalize(pAppidActiveConfig);
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ sipUaFinalize(&pAppidActiveConfig->detectorSipConfig);
+ ssl_detector_process_patterns(&pAppidActiveConfig->serviceSslConfig);
+#endif
+ dns_host_detector_process_patterns(&pAppidActiveConfig->serviceDnsConfig);
+ portPatternFinalize(pAppidActiveConfig);
+ ClientAppFinalize(pAppidActiveConfig);
+ ServiceFinalize(pAppidActiveConfig);
+ appIdStatsInit(mod_config);
+ ThirdPartyAppIDInit(mod_config);
+
+ show();
+
+ // FIXIT - do we still need to support this?
+ if (pAppidActiveConfig->mod_config->dump_ports)
+ {
+ dumpPorts(stdout, pAppidActiveConfig);
+ appInfoTableDump(pAppidActiveConfig);
+ exit(0);
+ }
+
+#ifdef DEBUG_APP_COMMON
+ DisplayPortConfig(pAppidActiveConfig);
+#endif
+ if (AppIdServiceStateInit(mod_config->memcap))
+ exit(-1);
+ config_state = RNA_FW_CONFIG_STATE_INIT;
+ return true;
+ }
+
+ return false;
+}
+
+static void ConfigItemFree(ConfigItem* ci)
+{
+ if (ci)
+ {
+ if (ci->name)
+ snort_free(ci->name);
+ if (ci->value)
+ snort_free(ci->value);
+ snort_free(ci);
+ }
+}
+
+static void cleanup_config(AppIdConfig* 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; i<APP_ID_PORT_ARRAY_SIZE; i++ )
+ {
+ if ( pConfig->tcp_port_exclusions_src[i] != nullptr )
+ {
+ sflist_free_all(pConfig->tcp_port_exclusions_src[i], &free);
+ pConfig->tcp_port_exclusions_src[i] = nullptr;
+ }
+ if ( pConfig->tcp_port_exclusions_dst[i] != nullptr )
+ {
+ sflist_free_all(pConfig->tcp_port_exclusions_dst[i], &free);
+ pConfig->tcp_port_exclusions_dst[i] = nullptr;
+ }
+ if ( pConfig->udp_port_exclusions_src[i] != nullptr )
+ {
+ sflist_free_all(pConfig->udp_port_exclusions_src[i], &free);
+ pConfig->udp_port_exclusions_src[i] = nullptr;
+ }
+ if ( pConfig->udp_port_exclusions_dst[i] != nullptr )
+ {
+ sflist_free_all(pConfig->udp_port_exclusions_dst[i], &free);
+ pConfig->udp_port_exclusions_dst[i] = nullptr;
+ }
+ }
+
+ pConfig->net_list = nullptr;
+
+ if (pConfig->CHP_glossary)
+ {
+ sfxhash_delete(pConfig->CHP_glossary);
+ pConfig->CHP_glossary = nullptr;
+ }
+
+ if (pConfig->AF_indicators)
+ {
+ sfxhash_delete(pConfig->AF_indicators);
+ pConfig->AF_indicators = nullptr;
+ }
+
+ if (pConfig->AF_actives)
+ {
+ sfxhash_delete(pConfig->AF_actives);
+ pConfig->AF_actives = nullptr;
+ }
+
+ memset(pConfig->net_list_by_zone, 0, sizeof(pConfig->net_list_by_zone));
+
+ sflist_static_free_all(&pConfig->client_app_args, (void (*)(void*))ConfigItemFree);
+}
+
+int AppIdConfig::cleanup(void)
+{
+ if (config_state == RNA_FW_CONFIG_STATE_INIT)
+ {
+ config_state = RNA_FW_CONFIG_STATE_PENDING;
+ if (thirdparty_appid_module != nullptr)
+ {
+ thirdparty_appid_module->print_stats();
+ }
+ ThirdPartyAppIDFini();
+
+ cleanup_config(pAppidActiveConfig);
+ CleanupServices(pAppidActiveConfig);
+ CleanupClientApp(pAppidActiveConfig);
+ LuaDetectorModuleManager::luaModuleFini();
+ hostPortAppCacheFini(pAppidActiveConfig);
+ AppIdServiceStateCleanup();
+ appIdStatsFini();
+ fwAppIdFini(pAppidActiveConfig);
+ http_detector_clean(&pAppidActiveConfig->detectorHttpConfig);
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ service_ssl_clean(&pAppidActiveConfig->serviceSslConfig);
+#endif
+ service_dns_host_clean(&pAppidActiveConfig->serviceDnsConfig);
+ config_state = RNA_FW_CONFIG_STATE_UNINIT;
+ snort_free(pAppidActiveConfig);
+ pAppidActiveConfig = nullptr;
+ return 0;
+ }
+ return -1;
+}
+
+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;
+ SF_LNODE* lnext;
+
+ if (!pe_list)
+ return;
+
+ for (pe = (PortExclusion*)sflist_first(pe_list, &lnext);
+ pe;
+ pe = (PortExclusion*)sflist_next(&lnext))
+ {
+ p = inet_ntop(pe->family, &pe->ip, inet_buffer, sizeof(inet_buffer));
+ p2 = inet_ntop(pe->family, &pe->netmask, inet_buffer2, sizeof(inet_buffer2));
+ LogMessage(" %d on %s/%s\n", port, p ? p : "ERROR", p2 ? p2 : "ERROR");
+ }
+}
+
+void AppIdConfig::show()
+{
+ 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* my_net_list;
+
+ if (mod_config->thirdparty_appid_dir)
+ LogMessage(" 3rd Party Dir: %s\n", mod_config->thirdparty_appid_dir);
+
+ my_net_list = net_list;
+ LogMessage(" Monitoring Networks for any zone:\n");
+ for (i = 0; i < my_net_list->count; i++)
+ {
+ ia.s_addr = htonl(my_net_list->pnetwork[i]->range_min);
+ p = inet_ntop(AF_INET, &ia, inet_buffer, sizeof(inet_buffer));
+ ia.s_addr = htonl(my_net_list->pnetwork[i]->range_max);
+ p2 = inet_ntop(AF_INET, &ia, inet_buffer2, sizeof(inet_buffer2));
+ LogMessage(" %s%s-%s %04X\n", (my_net_list->pnetwork[i]->info.ip_not) ? "!" : "",
+ p ?
+ p : "ERROR",
+ p2 ? p2 : "ERROR", my_net_list->pnetwork[i]->info.type);
+ }
+ for (i = 0; i < my_net_list->count6; i++)
+ {
+ six = my_net_list->pnetwork6[i]->range_min;
+ NSIPv6AddrHtoN(&six);
+ p = inet_ntop(AF_INET6, (struct in6_addr*)&six, inet_buffer, sizeof(inet_buffer));
+ six = my_net_list->pnetwork6[i]->range_max;
+ NSIPv6AddrHtoN(&six);
+ p2 = inet_ntop(AF_INET6, (struct in6_addr*)&six, inet_buffer2, sizeof(inet_buffer2));
+ LogMessage(" %s%s-%s %04X\n", (my_net_list->pnetwork6[i]->info.ip_not) ? "!" : "",
+ p ?
+ p : "ERROR",
+ p2 ? p2 : "ERROR", my_net_list->pnetwork6[i]->info.type);
+ }
+
+ for (j=0; j < MAX_ZONES; j++)
+ {
+ if (!(my_net_list = net_list_by_zone[j]))
+ continue;
+ LogMessage(" Monitoring Networks for zone %d:\n", j);
+ for (i = 0; i < my_net_list->count; i++)
+ {
+ ia.s_addr = htonl(my_net_list->pnetwork[i]->range_min);
+ p = inet_ntop(AF_INET, &ia, inet_buffer, sizeof(inet_buffer));
+ ia.s_addr = htonl(my_net_list->pnetwork[i]->range_max);
+ p2 = inet_ntop(AF_INET, &ia, inet_buffer2, sizeof(inet_buffer2));
+ LogMessage(" %s%s-%s %04X\n", (my_net_list->pnetwork[i]->info.ip_not) ? "!" :
+ "",
+ p ? p : "ERROR",
+ p2 ? p2 : "ERROR", my_net_list->pnetwork[i]->info.type);
+ }
+ for (i = 0; i < my_net_list->count6; i++)
+ {
+ six = my_net_list->pnetwork6[i]->range_min;
+ NSIPv6AddrHtoN(&six);
+ p = inet_ntop(AF_INET6, (struct in6_addr*)&six, inet_buffer, sizeof(inet_buffer));
+ six = my_net_list->pnetwork6[i]->range_max;
+ NSIPv6AddrHtoN(&six);
+ p2 = inet_ntop(AF_INET6, (struct in6_addr*)&six, inet_buffer2, sizeof(inet_buffer2));
+ LogMessage(" %s%s-%s %04X\n", (my_net_list->pnetwork6[i]->info.ip_not) ? "!" :
+ "",
+ p ? p : "ERROR",
+ p2 ? p2 : "ERROR", my_net_list->pnetwork6[i]->info.type);
+ }
+ }
+
+ LogMessage(" Excluded TCP Ports for Src:\n");
+ for (i = 0; i < APP_ID_PORT_ARRAY_SIZE; i++)
+ DisplayPortExclusionList(tcp_port_exclusions_src[i], i);
+
+ LogMessage(" Excluded TCP Ports for Dst:\n");
+ for (i = 0; i < APP_ID_PORT_ARRAY_SIZE; i++)
+ DisplayPortExclusionList(tcp_port_exclusions_dst[i], i);
+
+ LogMessage(" Excluded UDP Ports Src:\n");
+ for (i = 0; i < APP_ID_PORT_ARRAY_SIZE; i++)
+ DisplayPortExclusionList(udp_port_exclusions_src[i], i);
+
+ LogMessage(" Excluded UDP Ports Dst:\n");
+ for (i = 0; i < APP_ID_PORT_ARRAY_SIZE; i++)
+ DisplayPortExclusionList(udp_port_exclusions_dst[i], i);
+}
+
+#ifdef DEBUG_APP_COMMON
+static void DisplayPortConfig(AppIdConfig* aic)
+{
+ unsigned i;
+ int first;
+
+ first = 1;
+ for (i=0; i<sizeof(tcp_port_only)/sizeof(*tcp_port_only); i++)
+ {
+ if (tcp_port_only[i])
+ {
+ if (first)
+ {
+ LogMessage(" TCP Port-Only Services\n");
+ first = 0;
+ }
+ LogMessage(" %5u - %u\n", i, tcp_port_only[i]);
+ }
+ }
+
+ first = 1;
+ for (i=0; i<sizeof(udp_port_only)/sizeof(*udp_port_only); i++)
+ {
+ if (udp_port_only[i])
+ {
+ if (first)
+ {
+ LogMessage(" UDP Port-Only Services\n");
+ first = 0;
+ }
+ LogMessage(" %5u - %u\n", i, udp_port_only[i]);
+ }
+ }
+}
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// appid_config.h author Sourcefire Inc.
+
+#ifndef APP_ID_CONFIG_H
+#define APP_ID_CONFIG_H
+
+// AppId configuration data structures and access methods
+
+#include "client_plugins/client_app_config.h"
+#include "detector_plugins/detector_sip.h"
+#include "service_plugins/service_config.h"
+
+#include "appid.h"
+#include "http_common.h"
+
+
+#define APP_ID_MAX_DIRS 16
+#define APP_ID_PORT_ARRAY_SIZE 65536
+#define MAX_ZONES 1024
+
+// FIXIT - defined here but confirm where this is defined in 2.9.8
+#define MAX_EVENT_APPNAME_LEN 64
+
+struct NetworkSet;
+struct AppInfoTableEntry;
+struct DynamicArray;
+struct ServicePortPattern;
+struct ClientPortPattern;
+struct SFGHASH;
+struct SFXHASH;
+
+extern unsigned appIdPolicyId;
+extern uint32_t app_id_netmasks[];
+
+struct PortExclusion
+{
+ int family;
+ ip::snort_in6_addr ip;
+ ip::snort_in6_addr netmask;
+};
+
+// 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.
+struct AppidGenericConfigItem
+{
+ char* name; ///< Module name
+ void* pData; ///< Module configuration data
+};
+
+// FIXIT - these values come from struct AppidStaticConfig...that should go away when this all works
+class AppIdModuleConfig
+{
+public:
+ AppIdModuleConfig() {};
+ ~AppIdModuleConfig();
+
+ const char* conf_file = nullptr;
+ const char* app_stats_filename = nullptr;
+ unsigned long app_stats_period = 0;
+ unsigned long app_stats_rollover_size = 0;
+ unsigned long app_stats_rollover_time = 0;
+ const char* app_detector_dir = nullptr;
+ const char* thirdparty_appid_dir = nullptr;
+ uint32_t instance_id = 0;
+ uint32_t memcap = 0;
+ bool debug = false;
+ bool dump_ports = false;
+
+ // FIXIT - configs below not set from appid preproc config...
+ uint32_t disable_safe_search = 0;
+ uint32_t dns_host_reporting = 0;
+ uint32_t referred_appId_disabled = 0;
+ uint32_t rtmp_max_packets = 0;
+ uint32_t mdns_user_reporting = 0;
+ uint32_t ftp_userid_disabled = 0;
+ uint32_t chp_userid_disabled = 0;
+ uint32_t chp_body_collection_disabled = 0;
+ uint32_t chp_fflow_disabled = 0;
+ uint32_t chp_body_collection_max = 0;
+ uint32_t max_tp_flow_depth = 0;
+ uint32_t tp_allow_probes = 0;
+ bool http2_detection_enabled = false;
+};
+
+
+// App ID Active Configuration
+enum RnaFwConfigState
+{
+ RNA_FW_CONFIG_STATE_UNINIT,
+ RNA_FW_CONFIG_STATE_INIT,
+ RNA_FW_CONFIG_STATE_PENDING,
+};
+
+class AppIdConfig
+{
+public:
+ AppIdConfig( AppIdModuleConfig* config ) : mod_config( config ) { }
+ ~AppIdConfig() { cleanup(); }
+
+ bool init_appid();
+ int cleanup(void);
+ void show();
+
+ void set_safe_search_enforcement(int enabled);
+
+ // add, find, remove generic config items...
+ void add_generic_config_element(const char* name, void* pData);
+ void* find_generic_config_element(const char* name);
+ void remove_generic_config_element(const char* name);
+
+ unsigned max_service_info = 0;
+ unsigned net_list_count = 0;
+ NetworkSet* net_list_list = nullptr;
+ NetworkSet* net_list = nullptr;
+ NetworkSet* net_list_by_zone[MAX_ZONES] = { nullptr };
+ AppId tcp_port_only[65536] = { 0 }; ///< Service IDs for port-only TCP services
+ AppId udp_port_only[65536] = { 0 }; ///< Service IDs for port-only UDP services
+ AppId ip_protocol[255] = { 0 }; ///< Service IDs for non-TCP / UDP protocol services
+ SF_LIST client_app_args; ///< List of Client App arguments
+ // for each potential port, an sflist of PortExclusion structs
+ SF_LIST* tcp_port_exclusions_src[APP_ID_PORT_ARRAY_SIZE] = { nullptr };
+ SF_LIST* udp_port_exclusions_src[APP_ID_PORT_ARRAY_SIZE] = { nullptr };
+ SF_LIST* tcp_port_exclusions_dst[APP_ID_PORT_ARRAY_SIZE] = { nullptr };
+ SF_LIST* udp_port_exclusions_dst[APP_ID_PORT_ARRAY_SIZE] = { nullptr };
+ SFXHASH* CHP_glossary = nullptr; // keep track of http multipatterns here
+ SFXHASH* AF_indicators = nullptr; // App Forecasting list of "indicator apps"
+ SFXHASH* AF_actives = nullptr; // App Forecasting list of hosts to watch for forecast apps
+ AppInfoTableEntry* AppInfoList = nullptr;
+ AppInfoTableEntry* AppInfoTable[SF_APPID_MAX] = { nullptr };
+ AppInfoTableEntry* AppInfoTableByService[SF_APPID_MAX] = { nullptr };
+ AppInfoTableEntry* AppInfoTableByClient[SF_APPID_MAX] = { nullptr };
+ AppInfoTableEntry* AppInfoTableByPayload[SF_APPID_MAX] = { nullptr };
+ DynamicArray* AppInfoTableDyn = nullptr;
+ SFGHASH* AppNameHash = nullptr;
+ SFXHASH* hostPortCache = nullptr;
+ SFXHASH* lengthCache = nullptr;
+ DetectorHttpConfig detectorHttpConfig; // HTTP detector configuration
+ DetectorSipConfig detectorSipConfig; // SIP detector configuration
+ ServiceConfig serviceConfig; // Common configuration for all services
+ ServiceSslConfig serviceSslConfig; // SSL service configuration
+ ServiceDnsConfig serviceDnsConfig; // DNS service configuration
+ ClientAppConfig clientAppConfig; // Common configuration for all client applications
+ HttpPatternLists httpPatternLists;
+ ServicePortPattern* servicePortPattern = nullptr;
+ ClientPortPattern* clientPortPattern = nullptr;
+ SF_LIST genericConfigList; ///< List of AppidGenericConfigItem structures
+ AppIdModuleConfig* mod_config;
+
+private:
+ int init_AF_indicators();
+ int init_AF_actives();
+ int init_CHP_glossary();
+ void load_modules(uint32_t instance_id);
+ void finalize_pattern_modules();
+ void read_port_detectors(const char* files);
+ void configure_analysis_networks(char* toklist[], uint32_t flag);
+ int add_port_exclusion(SF_LIST* port_exclusions[], const ip::snort_in6_addr* ip,
+ const ip::snort_in6_addr* netmask, int family, uint16_t port);
+ void process_port_exclusion(char* toklist[]);
+ void process_config_directive(char* toklist[], int /* reload */);
+ int load_analysis_config(const char* config_file, int reload, int instance_id);
+
+ RnaFwConfigState config_state = RNA_FW_CONFIG_STATE_UNINIT;
+};
+
+// FIXIT - this global needs to go asap... just here now to compile while doing some major config refactoring
+extern AppIdConfig* pAppidActiveConfig;
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// appid_flow_data.cc author Sourcefire Inc.
+
+#include "appid_flow_data.h"
+#include "fw_appid.h"
+#include "appid_stats.h"
+#include "service_plugins/service_base.h"
+
+#include "log/messages.h"
+#include "stream/stream_api.h"
+#include "sfip/sf_ip.h"
+#include "utils/util.h"
+
+unsigned AppIdData::flow_id = 0;
+
+static AppIdFlowData* fd_free_list;
+
+AppIdData::~AppIdData()
+{
+ appSharedDataDelete();
+}
+
+void AppIdData::appHttpFieldClear()
+{
+ if (hsession == nullptr)
+ return;
+
+ if (hsession->referer)
+ {
+ snort_free(hsession->referer);
+ hsession->referer = nullptr;
+ }
+ if (hsession->cookie)
+ {
+ snort_free(hsession->cookie);
+ hsession->cookie = nullptr;
+ }
+ if (hsession->url)
+ {
+ snort_free(hsession->url);
+ hsession->url = nullptr;
+ }
+ if (hsession->useragent)
+ {
+ snort_free(hsession->useragent);
+ hsession->useragent = nullptr;
+ }
+ if (hsession->host)
+ {
+ snort_free(hsession->host);
+ hsession->host = nullptr;
+ }
+ if (hsession->uri)
+ {
+ snort_free(hsession->uri);
+ hsession->uri = nullptr;
+ }
+ if (hsession->content_type)
+ {
+ snort_free(hsession->content_type);
+ hsession->content_type = nullptr;
+ }
+ if (hsession->location)
+ {
+ snort_free(hsession->location);
+ hsession->location = nullptr;
+ }
+ if (hsession->body)
+ {
+ snort_free(hsession->body);
+ hsession->body = nullptr;
+ }
+ if (hsession->req_body)
+ {
+ snort_free(hsession->req_body);
+ hsession->req_body = nullptr;
+ }
+ if (hsession->xffAddr)
+ {
+ sfip_free(hsession->xffAddr);
+ hsession->xffAddr = nullptr;
+ }
+}
+
+void AppIdData::appHttpSessionDataFree()
+{
+ int i;
+
+ if (hsession == nullptr)
+ return;
+
+ appHttpFieldClear();
+
+ for (i = 0; i < NUMBER_OF_PTYPES; i++)
+ {
+ if (nullptr != hsession->new_field[i])
+ {
+ snort_free(hsession->new_field[i]);
+ hsession->new_field[i] = nullptr;
+ }
+ }
+ if (hsession->fflow)
+ {
+ snort_free(hsession->fflow);
+ hsession->fflow = nullptr;
+ }
+ if (hsession->via)
+ {
+ snort_free(hsession->via);
+ hsession->via = nullptr;
+ }
+ if (hsession->content_type)
+ {
+ snort_free(hsession->content_type);
+ hsession->content_type = nullptr;
+ }
+ if (hsession->response_code)
+ {
+ snort_free(hsession->response_code);
+ hsession->response_code = nullptr;
+ }
+
+ snort_free(hsession);
+ hsession = nullptr;
+}
+
+void AppIdData::appDNSSessionDataFree()
+{
+ if (dsession )
+ {
+ if (dsession->host)
+ {
+ snort_free(dsession->host);
+ dsession->host = nullptr;
+ }
+ snort_free(dsession);
+ dsession = nullptr;
+ }
+}
+
+void AppIdData::appTlsSessionDataFree()
+{
+ if (tsession )
+ {
+ if (tsession->tls_host)
+ snort_free(tsession->tls_host);
+ if (tsession->tls_cname)
+ snort_free(tsession->tls_cname);
+ if (tsession->tls_orgUnit)
+ snort_free(tsession->tls_orgUnit);
+ snort_free(tsession);
+ tsession = nullptr;
+ }
+}
+
+void AppIdData::AppIdFlowdataFree()
+{
+ AppIdFlowData* tmp_fd;
+
+ while ((tmp_fd = flowData))
+ {
+ 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;
+ }
+}
+
+void AppIdData::appSharedDataDelete()
+{
+ RNAServiceSubtype* rna_service_subtype;
+
+ /*check daq flag */
+ appIdStatsUpdate(this);
+
+ if (ssn)
+ FailInProcessService(this, pAppidActiveConfig);
+ AppIdFlowdataFree();
+
+ if (thirdparty_appid_module)
+ {
+ thirdparty_appid_module->session_delete(tpsession, 0);
+ tpsession = nullptr;
+ }
+
+ snort_free(clientVersion);
+ snort_free(serviceVendor);
+ snort_free(serviceVersion);
+ snort_free(netbios_name);
+ while ((rna_service_subtype = subtype))
+ {
+ subtype = rna_service_subtype->next;
+ snort_free(*(void**)&rna_service_subtype->service);
+ snort_free(*(void**)&rna_service_subtype->vendor);
+ snort_free(*(void**)&rna_service_subtype->version);
+ snort_free(rna_service_subtype);
+ }
+ if (candidate_service_list)
+ {
+ sflist_free(candidate_service_list);
+ candidate_service_list = nullptr;
+ }
+
+ if (candidate_client_list)
+ {
+ sflist_free(candidate_client_list);
+ candidate_client_list = nullptr;
+ }
+ snort_free(username);
+ snort_free(netbiosDomain);
+ snort_free(payloadVersion);
+ appHttpSessionDataFree();
+ appTlsSessionDataFree();
+ appDNSSessionDataFree();
+ tsession = nullptr;
+
+ snort_free(firewallEarlyData);
+ firewallEarlyData = nullptr;
+
+ // should be freed by flow
+ // appSharedDataFree(sharedData);
+}
+
+void AppIdFlowdataFini()
+{
+ AppIdFlowData* tmp_fd;
+
+ while ((tmp_fd = fd_free_list))
+ {
+ fd_free_list = fd_free_list->next;
+ snort_free(tmp_fd);
+ }
+}
+
+void* AppIdFlowdataGet(AppIdData* 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 : nullptr;
+}
+
+void* AppIdFlowdataRemove(AppIdData* 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;
+ return fd->fd_data;
+ }
+ return nullptr;
+}
+
+void AppIdFlowdataDelete(AppIdData* 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;
+ }
+}
+
+void AppIdFlowdataDeleteAllByMask(AppIdData* 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;
+ }
+ else
+ {
+ pfd = &(*pfd)->next;
+ }
+ }
+}
+
+int AppIdFlowdataAdd(AppIdData* 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;
+ }
+ else
+ tmp_fd = (AppIdFlowData*)snort_alloc(sizeof(AppIdFlowData));
+
+ 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(AppIdData* flowp, uint16_t port, const RNAServiceElement* 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(AppIdData* sharedData)
+{
+ _dpd.errMsg("Deleting %p\n",sharedData);
+ appSharedDataDelete(sharedData);
+}
+
+#endif
+
+AppIdData* AppIdEarlySessionCreate(
+ AppIdData*, const Packet* /*ctrlPkt*/, const sfip_t* cliIp, uint16_t cliPort, const
+ sfip_t* srvIp,
+ uint16_t srvPort, IpProtocol proto, int16_t app_id, int /*flags*/)
+{
+ char src_ip[INET6_ADDRSTRLEN];
+ char dst_ip[INET6_ADDRSTRLEN];
+ // FIXIT - not needed until crtlPkt expectedSession is supported
+ //struct _ExpectNode** node;
+ AppIdData* data;
+ enum PktType protocol = ( enum PktType )proto;
+
+ if (app_id_debug_session_flag)
+ {
+ sfip_ntop(cliIp, src_ip, sizeof(src_ip));
+ sfip_ntop(srvIp, dst_ip, sizeof(dst_ip));
+ }
+
+ data = appSharedDataAlloc(proto, cliIp);
+ if (data)
+ data->common.policyId = appIdPolicyId;
+
+ // FIXIT - expect session control packet support not ported to snort3 yet
+ //node = (flags & APPID_EARLY_SESSION_FLAG_FW_RULE) ? &ctrlPkt->expectedSession : nullptr;
+
+ // FIXIT - 2.9.x set_application_protocol_id_expected has several new parameters, need to look
+ // into what is required to support those here.
+ if (stream.set_application_protocol_id_expected(/*crtlPkt,*/ cliIp, cliPort, srvIp, srvPort,
+ protocol, app_id, data) )
+ {
+ if (app_id_debug_session_flag)
+ LogMessage("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);
+ data->appSharedDataDelete();
+ return nullptr;
+ }
+ else if (app_id_debug_session_flag)
+ LogMessage("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;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// appid_flow_data.h author Sourcefire Inc.
+
+#ifndef APPID_SESSION_H
+#define APPID_SESSION_H
+
+// AppId configuration data structures and access methods
+
+#include <cstdint>
+#include <ctime>
+
+#include "protocols/packet.h"
+#include "utils/sflsq.h"
+
+#include "appid.h"
+#include "appid_api.h"
+#include "application_ids.h"
+#include "flow_error.h"
+#include "length_app_cache.h"
+#include "service_state.h"
+#include "thirdparty_appid_api.h"
+#include "thirdparty_appid_types.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;
+struct RNAServiceSubtype;
+struct RNAClientAppModule;
+
+enum RNA_INSPECTION_STATE
+{
+ RNA_STATE_NONE = 0,
+ RNA_STATE_DIRECT,
+ RNA_STATE_STATEFUL,
+ RNA_STATE_FINISHED
+};
+
+using AppIdFreeFCN = void(*)(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)
+
+struct AppIdFlowData
+{
+ AppIdFlowData* next;
+ unsigned fd_id;
+ void* fd_data;
+ AppIdFreeFCN fd_free;
+};
+
+#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
+
+struct APPID_SESSION_STRUCT_FLAG
+{
+ APPID_FLOW_TYPE flow_type;
+};
+
+struct CommonAppIdData
+{
+ APPID_SESSION_STRUCT_FLAG fsf_type; /* This must be first. */
+ unsigned policyId;
+ //flags shared with other preprocessor via session attributes.
+ uint64_t flags;
+ sfip_t initiator_ip;
+ uint16_t initiator_port;
+};
+
+#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_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)
+
+struct fflow_info
+{
+ uint32_t sip;
+ uint32_t dip;
+ uint16_t sport;
+ uint16_t dport;
+ IpProtocol protocol;
+ AppId appId;
+ int flow_prepared;
+};
+
+struct HttpRewriteableFields
+{
+ char* str;
+};
+
+struct httpSession
+{
+ char* host;
+ char* url;
+ char* uri;
+ char* via;
+ char* useragent;
+ char* response_code;
+ char* referer;
+ char* cookie;
+ char* content_type;
+ char* location;
+ char* body;
+ char* req_body;
+ char* server;
+ char* x_working_with;
+ char* new_field[HTTP_FIELD_MAX+1];
+
+ uint16_t uriOffset;
+ uint16_t uriEndOffset;
+ uint16_t cookieOffset;
+ uint16_t cookieEndOffset;
+
+ fflow_info* fflow;
+
+ int chp_finished;
+ AppId chp_candidate;
+ AppId chp_alt_candidate;
+ int chp_hold_flow;
+ int ptype_req_counts[NUMBER_OF_PTYPES];
+ int total_found;
+ unsigned app_type_flags;
+ int num_matches;
+ int num_scans;
+ int get_offsets_from_rebuilt;
+ bool skip_simple_detect; // Flag to indicate if simple detection of client ID, payload ID,
+ // etc
+ // should be skipped
+ sfip_t* xffAddr;
+ const char** xffPrecedence;
+ int numXffFields;
+
+#if RESPONSE_CODE_PACKET_THRESHHOLD
+ unsigned response_code_packets;
+#endif
+};
+
+// For dnsSession.state:
+#define DNS_GOT_QUERY 0x01
+#define DNS_GOT_RESPONSE 0x02
+
+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
+ uint32_t ttl; // response: TTL
+ char* host; // host (usually query, but could be response for reverse lookup)
+};
+
+struct _RNAServiceSubtype;
+
+struct tlsSession
+{
+ char* tls_host;
+ int tls_host_strlen;
+ char* tls_cname;
+ int tls_cname_strlen;
+ char* tls_orgUnit;
+ int tls_orgUnit_strlen;
+};
+
+class AppIdData : public FlowData
+{
+public:
+ AppIdData() : FlowData(flow_id) { service_ip.clear(); }
+ ~AppIdData();
+
+ void reset()
+ { *this = AppIdData(); }
+
+
+ CommonAppIdData common;
+ AppIdData* next = nullptr;
+ Flow* ssn = nullptr;
+
+ sfip_t service_ip;
+ uint16_t service_port = 0;
+ IpProtocol proto = IpProtocol::PROTO_NOT_SET;
+ uint8_t previous_tcp_flags = 0;
+
+ AppIdFlowData* flowData = nullptr;
+
+ // AppId matching service side
+ AppId serviceAppId = APP_ID_NONE;
+ AppId portServiceAppId = APP_ID_NONE;
+ // RNAServiceElement for identifying detector
+ const RNAServiceElement* serviceData = nullptr;
+ RNA_INSPECTION_STATE rnaServiceState = RNA_STATE_NONE;
+ char* serviceVendor = nullptr;
+ char* serviceVersion = nullptr;
+ RNAServiceSubtype* subtype = nullptr;
+ AppIdServiceIDState* id_state = nullptr;
+ char* netbios_name = nullptr;
+ SF_LIST* candidate_service_list = nullptr;
+ unsigned int num_candidate_services_tried = 0;
+ int got_incompatible_services = 0;
+
+ /**AppId matching client side */
+ AppId ClientAppId = APP_ID_NONE;
+ AppId ClientServiceAppId = APP_ID_NONE;
+ char* clientVersion = nullptr;
+ /**RNAClientAppModule for identifying client detector*/
+ const RNAClientAppModule* clientData = nullptr;
+ RNA_INSPECTION_STATE rnaClientState = RNA_STATE_NONE;
+ SF_LIST* candidate_client_list = nullptr;
+ unsigned int num_candidate_clients_tried = 0;
+ bool tried_reverse_service = false;
+
+ /**AppId matching payload*/
+ AppId payloadAppId = APP_ID_NONE;
+ AppId referredPayloadAppId = APP_ID_NONE;
+ AppId miscAppId = APP_ID_NONE;
+
+ //appId determined by 3rd party library
+ AppId tpAppId = APP_ID_NONE;
+ AppId tpPayloadAppId = APP_ID_NONE;
+
+ char* username = nullptr;
+ AppId usernameService = APP_ID_NONE;
+
+ char* netbiosDomain = nullptr;
+
+ uint32_t id = 0;
+
+ httpSession* hsession = nullptr;
+ tlsSession* tsession = nullptr;
+
+ unsigned scan_flags = 0;
+#if RESPONSE_CODE_PACKET_THRESHHOLD
+ unsigned response_code_packets = 0;
+#endif
+
+ AppId referredAppId = APP_ID_NONE;
+
+ AppId tmpAppId = APP_ID_NONE;
+ void* tpsession = nullptr;
+ uint16_t init_tpPackets = 0;
+ uint16_t resp_tpPackets = 0;
+ uint8_t tpReinspectByInitiator = 0;
+ char* payloadVersion = nullptr;
+
+ uint16_t session_packet_count = 0;
+ int16_t snortId = 0;
+
+ /* Length-based detectors. */
+ LengthKey length_sequence;
+
+ struct
+ {
+ uint32_t firstPktsecond;
+ uint32_t lastPktsecond;
+ uint64_t initiatorBytes;
+ uint64_t responderBytes;
+ } stats = {0, 0, 0, 0};
+
+ // Policy and rule ID for related flows (e.g. ftp-data)
+ AppIdData* expectedFlow = nullptr;
+
+ //appIds picked from encrypted session.
+ struct
+ {
+ AppId serviceAppId;
+ AppId ClientAppId;
+ AppId payloadAppId;
+ AppId miscAppId;
+ AppId referredAppId;
+ } encrypted = { APP_ID_NONE, APP_ID_NONE, APP_ID_NONE, APP_ID_NONE, APP_ID_NONE };
+
+ // New fields introduced for DNS Blacklisting
+ dnsSession* dsession = nullptr;
+
+ void* firewallEarlyData = nullptr;
+ AppId pastIndicator = APP_ID_NONE;
+ AppId pastForecast = APP_ID_NONE;
+
+ bool is_http2 = false;
+ SEARCH_SUPPORT_TYPE search_support_type = SEARCH_SUPPORT_TYPE_UNKNOWN;
+
+ static unsigned flow_id;
+ static void init() { flow_id = FlowData::get_flow_id(); }
+
+ void appHttpFieldClear();
+ void appHttpSessionDataFree();
+ void appDNSSessionDataFree();
+ void appTlsSessionDataFree();
+ void AppIdFlowdataFree();
+ void appSharedDataDelete();
+};
+
+inline void setAppIdFlag(AppIdData* flow, uint64_t flags)
+{ flow->common.flags |= flags; }
+
+inline void clearAppIdFlag(AppIdData* flow, uint64_t flags)
+{ flow->common.flags &= ~flags; }
+
+inline uint64_t getAppIdFlag(AppIdData* flow, uint64_t flags)
+{ return (flow->common.flags & flags); }
+
+void AppIdFlowdataFini();
+void* AppIdFlowdataGet(AppIdData*, unsigned id);
+int AppIdFlowdataAdd(AppIdData*, void* data, unsigned id, AppIdFreeFCN);
+void* AppIdFlowdataRemove(AppIdData*, unsigned id);
+void AppIdFlowdataDelete(AppIdData*, unsigned id);
+void AppIdFlowdataDeleteAllByMask(AppIdData*, unsigned mask);
+AppIdData* AppIdEarlySessionCreate(AppIdData*, const Packet* ctrlPkt, const sfip_t* cliIp,
+ uint16_t cliPort, const sfip_t* srvIp, uint16_t srvPort, IpProtocol proto, int16_t app_id, int flags);
+int AppIdFlowdataAddId(AppIdData*, uint16_t port, const RNAServiceElement*);
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2016-2016 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.
+//--------------------------------------------------------------------------
+
+// appid_inspector.cc author davis mcpherson <davmcphe@cisco.com>
+// Created on: May 10, 2016
+
+#include "appid_inspector.h"
+#include "profiler/profiler.h"
+#include "fw_appid.h"
+
+//-------------------------------------------------------------------------
+// class stuff
+//-------------------------------------------------------------------------
+
+AppIdInspector::AppIdInspector(const AppIdModuleConfig* pc)
+{
+ assert(pc);
+ config = pc;
+}
+
+AppIdInspector::~AppIdInspector()
+{
+ delete config;
+}
+
+bool AppIdInspector::configure(SnortConfig*)
+{
+ active_config = new AppIdConfig( ( AppIdModuleConfig* )config);
+ return active_config->init_appid();
+
+ // FIXIT some of this stuff may be needed in some fashion...
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ _dpd.registerGeAppId(getOpenAppId);
+ if (!thirdparty_appid_module)
+ _dpd.streamAPI->register_http_header_callback(httpHeaderCallback);
+ _dpd.registerSslAppIdLookup(sslAppGroupIdLookup);
+
+ // FIXIT AppID will need to register for SIP events for sip detection to work...
+ if (_dpd.streamAPI->service_event_subscribe(PP_SIP, SIP_EVENT_TYPE_SIP_DIALOG,
+ SipSessionSnortCallback) == false)
+ DynamicPreprocessorFatalMessage("failed to subscribe to SIP_DIALOG\n");
+#endif
+}
+
+void AppIdInspector::show(SnortConfig*)
+{
+ LogMessage("AppId Configuration\n");
+
+ LogMessage(" Detector Path: %s\n", config->app_detector_dir);
+ LogMessage(" appStats Files: %s\n", config->app_stats_filename);
+ LogMessage(" appStats Period: %lu secs\n", config->app_stats_period);
+ LogMessage(" appStats Rollover Size: %lu bytes\n",
+ config->app_stats_rollover_size);
+ LogMessage(" appStats Rollover time: %lu secs\n",
+ config->app_stats_rollover_time);
+ LogMessage("\n");
+}
+
+void AppIdInspector::eval(Packet* pkt)
+{
+ Profile profile(appidPerfStats);
+
+ appid_stats.packet_count++;
+ fwAppIdSearch(pkt);
+}
+
+//-------------------------------------------------------------------------
+// api stuff
+//-------------------------------------------------------------------------
+
+static Module* mod_ctor()
+{
+ return new AppIdModule;
+}
+
+static void mod_dtor(Module* m)
+{
+ delete m;
+}
+
+static void appid_inspector_init()
+{
+ AppIdData::init();
+}
+
+static Inspector* appid_inspector_ctor(Module* m)
+{
+ AppIdModule* mod = (AppIdModule*)m;
+ return new AppIdInspector(mod->get_data());
+}
+
+static void appid_inspector_dtor(Inspector* p)
+{
+ delete p;
+}
+
+const InspectApi appid_inspector_api =
+{
+ {
+ PT_INSPECTOR,
+ sizeof(InspectApi),
+ INSAPI_VERSION,
+ 0,
+ API_RESERVED,
+ API_OPTIONS,
+ MOD_NAME,
+ MOD_HELP,
+ mod_ctor,
+ mod_dtor
+ },
+ IT_NETWORK,
+ (uint16_t)PktType::ANY_IP,
+ nullptr, // buffers
+ nullptr, // service
+ appid_inspector_init, // pinit
+ nullptr, // pterm
+ nullptr, // tinit
+ nullptr, // tterm
+ appid_inspector_ctor,
+ appid_inspector_dtor,
+ nullptr, // ssn
+ nullptr // reset
+};
+
+#ifdef BUILDING_SO
+SO_PUBLIC const BaseApi* snort_plugins[] =
+{
+ &appid_inspector_api.base,
+ nullptr
+};
+#else
+const BaseApi* nin_appid_inspector = &appid_inspector_api.base;
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2016-2016 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.
+//--------------------------------------------------------------------------
+
+// appid_inspector.h author davis mcpherson <davmcphe@cisco.com>
+// Created on: May 10, 2016
+
+#ifndef APPID_INSPECTOR_H
+#define APPID_INSPECTOR_H
+
+#include "main/snort_config.h"
+#include "protocols/packet.h"
+
+#include "appid_module.h"
+
+class AppIdInspector : public Inspector
+{
+public:
+
+ AppIdInspector(const AppIdModuleConfig*);
+ ~AppIdInspector();
+
+ bool configure(SnortConfig*) override;
+ void show(SnortConfig*) override;
+ void eval(Packet*) override;
+
+ AppIdConfig* get_active_config()
+ {
+ return active_config;
+ }
+
+private:
+
+ const AppIdModuleConfig* config = nullptr;
+ AppIdConfig* active_config = nullptr;
+};
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2016-2016 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.
+//--------------------------------------------------------------------------
+
+// appid_module.cc author davis mcpherson <davmcphe@cisco.com>
+// Created on: May 10, 2016
+
+#include <string>
+
+#include "appid_module.h"
+#include "profiler/profiler.h"
+#include "utils/util.h"
+
+using namespace std;
+
+//-------------------------------------------------------------------------
+// appid module
+//-------------------------------------------------------------------------
+
+THREAD_LOCAL ProfileStats appidPerfStats;
+
+const PegInfo appid_pegs[] =
+{
+ { "packet_count", "count of packets processed by appid" },
+ { "dns_udp_count", "count of dns flows over udp discovered" },
+ { "dns_tcp_count", "count of dns flows over tcp discovered" },
+ { "smtp_count", "count of smtp flows discovered" },
+ { "smtps_count", "count of smtps flows discovered" },
+ { nullptr, nullptr }
+};
+
+THREAD_LOCAL AppIdStats appid_stats;
+
+static const Parameter s_params[] =
+{
+ { "conf", Parameter::PT_STRING, nullptr, nullptr,
+ "RNA configuration file" },
+ { "memcap", Parameter::PT_INT, "1048576:3221225472", "268435456",
+ "time period for collecting and logging AppId statistics" },
+ { "app_stats_filename", Parameter::PT_STRING, nullptr, nullptr,
+ "Filename for logging AppId statistics" },
+ { "app_stats_period", Parameter::PT_INT, "0:", "300",
+ "time period for collecting and logging AppId statistics" },
+ { "app_stats_rollover_size", Parameter::PT_INT, "0:", "20971520",
+ "max file size for AppId stats before rolling over the log file" },
+ { "app_stats_rollover_time", Parameter::PT_INT, "0:", "86400",
+ "max time period for collection AppId stats before rolling over the log file" },
+ { "app_detector_dir", Parameter::PT_STRING, nullptr, nullptr,
+ "directory to load AppId detectors from" },
+ { "instance_id", Parameter::PT_INT, "0:", "0",
+ "instance id - need more details for what this is" },
+ { "debug", Parameter::PT_BOOL, nullptr, "false",
+ "enable AppId debug logging" },
+ { "dump_ports", Parameter::PT_BOOL, nullptr, "false",
+ "enable dump of AppId port information" },
+ { "thirdparty_appid_dir", Parameter::PT_STRING, nullptr, nullptr,
+ "directory to load thirdparty AppId detectors from" },
+
+ { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
+};
+
+// FIXIT-M: Add appid_rules back in once we start using it.
+#ifdef REMOVED_WHILE_NOT_IN_USE
+static const RuleMap appid_rules[] =
+{
+ { 0 /* rule id */, "description" },
+ { 0, nullptr }
+};
+#endif
+
+AppIdModule::AppIdModule() :
+ Module(MOD_NAME, MOD_HELP, s_params)
+{
+ config = nullptr;
+}
+
+AppIdModule::~AppIdModule()
+{
+}
+
+ProfileStats* AppIdModule::get_profile() const
+{
+ return &appidPerfStats;
+}
+
+const AppIdModuleConfig* AppIdModule::get_data()
+{
+ AppIdModuleConfig* temp = config;
+ config = nullptr;
+ return temp;
+}
+
+bool AppIdModule::set(const char*, Value& v, SnortConfig*)
+{
+ if ( v.is("conf") )
+ config->conf_file = snort_strdup(v.get_string());
+ else if ( v.is("memcap") )
+ config->memcap = v.get_long();
+ else if ( v.is("app_stats_filename") )
+ config->app_stats_filename = snort_strdup(v.get_string());
+ else if ( v.is("app_stats_period") )
+ config->app_stats_period = v.get_long();
+ else if ( v.is("app_stats_rollover_size") )
+ config->app_stats_rollover_size = v.get_long();
+ else if ( v.is("app_stats_rollover_time") )
+ config->app_stats_rollover_time = v.get_long();
+ else if ( v.is("app_detector_dir") )
+ config->app_detector_dir = snort_strdup(v.get_string());
+ else if ( v.is("thirdparty_appid_dir") )
+ config->thirdparty_appid_dir = snort_strdup(v.get_string());
+ else if ( v.is("instance_id") )
+ config->instance_id = v.get_long();
+ else if ( v.is("debug") )
+ config->debug = v.get_bool();
+ else if ( v.is("dump_ports") )
+ config->dump_ports = v.get_bool();
+ else
+ return false;
+
+ return true;
+}
+
+bool AppIdModule::begin(const char* /*fqn*/, int, SnortConfig*)
+{
+ if ( config )
+ return false;
+
+ config = new AppIdModuleConfig;
+ return true;
+}
+
+bool AppIdModule::end(const char*, int, SnortConfig*)
+{
+ return true;
+}
+
+const PegInfo* AppIdModule::get_pegs() const
+{
+ return appid_pegs;
+}
+
+PegCount* AppIdModule::get_counts() const
+{
+ return (PegCount*)&appid_stats;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2016-2016 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.
+//--------------------------------------------------------------------------
+
+// appid_module.h author davis mcpherson <davmcphe@cisco.com>
+// Created on: May 10, 2016
+
+#ifndef APPID_MODULE_H
+#define APPID_MODULE_H
+
+#include "main/snort_types.h"
+#include "framework/module.h"
+#include "appid_config.h"
+
+extern THREAD_LOCAL ProfileStats appidPerfStats;
+
+//-------------------------------------------------------------------------
+// stream module
+//-------------------------------------------------------------------------
+
+#define MOD_NAME "appid"
+#define MOD_HELP "application and service identification"
+
+struct AppIdStats
+{
+ PegCount packet_count;
+ PegCount dns_udp_count;
+ PegCount dns_tcp_count;
+ PegCount smtp_count;
+ PegCount smtps_count;
+};
+
+extern THREAD_LOCAL AppIdStats appid_stats;
+
+class AppIdModule : public Module
+{
+public:
+ AppIdModule();
+ ~AppIdModule();
+
+ bool begin(const char*, int, SnortConfig*) override;
+ bool set(const char*, Value&, SnortConfig*) override;
+ bool end(const char*, int, SnortConfig*) override;
+
+ const PegInfo* get_pegs() const override;
+ PegCount* get_counts() const override;
+ ProfileStats* get_profile() const override;
+
+ const AppIdModuleConfig* get_data();
+
+private:
+ AppIdModuleConfig* config;
+};
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// appid_stats.cc author Sourcefire Inc.
+
+#include "appid_stats.h"
+
+#include <cerrno>
+#include <cstdio>
+#include <ctime>
+#include <cstdint>
+
+#include "log/messages.h"
+#include "loggers/unified2_common.h"
+#include "utils/sflsq.h"
+#include "utils/util.h"
+
+#include "appid_api.h"
+#include "appid_flow_data.h"
+#include "fw_appid.h"
+#include "util/fw_avltree.h"
+#include "util/output_file.h"
+
+#define URLCATBUCKETS 100
+#define URLREPBUCKETS 5
+
+// FIXIT - find out where this is defined in snort 2.x and define appropriately here
+#if 1
+#define UNIFIED2_IDS_EVENT_APPSTAT 1
+#endif
+
+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;
+ FwAvlTree* appsTree;
+ struct
+ {
+ size_t txByteCnt;
+ size_t rxByteCnt;
+ } totalStats;
+ uint32_t appRecordCnt;
+};
+
+static SF_LIST* currBuckets;
+static SF_LIST* logBuckets;
+
+static const 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)
+{ snort_free(record); }
+
+static inline time_t get_time()
+{
+ auto now = time(nullptr);
+ return now - (now % bucketInterval);
+}
+
+void appIdStatsUpdate(AppIdData* session)
+{
+ if ( !enableAppStats )
+ return;
+
+ time_t now = get_time();
+
+ if (now >= bucketEnd)
+ {
+ endStats2Period();
+ dumpStats2();
+ startStats2Period(now);
+ }
+
+ time_t bucketTime = session->stats.firstPktsecond -
+ (session->stats.firstPktsecond % bucketInterval);
+
+ StatsBucket* bucket = getStatsBucket(bucketTime);
+ if ( !bucket )
+ return;
+
+ bucket->totalStats.txByteCnt += session->stats.initiatorBytes;
+ bucket->totalStats.rxByteCnt += session->stats.responderBytes;
+
+ const uint32_t web_app_id = pickPayloadId(session);
+ if (web_app_id > APP_ID_NONE)
+ {
+ const uint32_t app_id = web_app_id;
+ AppIdStatRecord* record = (AppIdStatRecord*)fwAvlLookup(app_id, bucket->appsTree);
+ if ( !record )
+ {
+ record = (AppIdStatRecord*)snort_calloc(sizeof(struct AppIdStatRecord));
+ 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
+ {
+ // FIXIT-M really? we just silently ignore an allocation failure?
+ snort_free(record);
+ record = nullptr;
+ }
+ }
+
+ if (record)
+ {
+ record->initiatorBytes += session->stats.initiatorBytes;
+ record->responderBytes += session->stats.responderBytes;
+ }
+ }
+
+ const uint32_t service_app_id = pickServiceAppId(session);
+ if ((service_app_id) &&
+ (service_app_id != web_app_id))
+ {
+ const uint32_t app_id = service_app_id;
+ AppIdStatRecord* record = (AppIdStatRecord*)fwAvlLookup(app_id, bucket->appsTree);
+ if ( !record )
+ {
+ record = (AppIdStatRecord*)snort_calloc(sizeof(struct AppIdStatRecord));
+ 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
+ {
+ // FIXIT-M really? don't ignore insert failure? add a stat here
+ snort_free(record);
+ record = nullptr;
+ }
+ }
+
+ if (record)
+ {
+ record->initiatorBytes += session->stats.initiatorBytes;
+ record->responderBytes += session->stats.responderBytes;
+ }
+ }
+
+ const uint32_t client_app_id = pickClientAppId(session);
+ if (client_app_id > APP_ID_NONE
+ && client_app_id != service_app_id
+ && client_app_id != web_app_id)
+ {
+ const uint32_t app_id = client_app_id;
+
+ AppIdStatRecord* record = (AppIdStatRecord*)fwAvlLookup(app_id, bucket->appsTree);
+ if ( !record )
+ {
+ record = (AppIdStatRecord*)snort_calloc(sizeof(struct AppIdStatRecord));
+ 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
+ {
+ // FIXIT-M really? we just silently ignore an allocation failure?
+ snort_free(record);
+ record = nullptr;
+ }
+ }
+
+ if (record)
+ {
+ record->initiatorBytes += session->stats.initiatorBytes;
+ record->responderBytes += session->stats.responderBytes;
+ }
+ }
+}
+
+void appIdStatsInit(AppIdModuleConfig* config)
+{
+ if (config->app_stats_filename)
+ {
+ enableAppStats = true;
+ appFilePath = config->app_stats_filename;
+
+ rollPeriod = config->app_stats_rollover_time;
+ rollSize = config->app_stats_rollover_size;
+ bucketInterval = config->app_stats_period;
+
+ time_t now = get_time();
+ startStats2Period(now);
+ appfp = nullptr;
+ }
+ else
+ enableAppStats = false;
+}
+
+static void appIdStatsCloseFiles()
+{
+ if (appfp)
+ {
+ fclose(appfp);
+ appfp = nullptr;
+ }
+}
+
+void appIdStatsReinit()
+{
+ // FIXIT-L J really should something like:
+ // if ( !stats_files_are_open() )
+ // return;
+ if (!enableAppStats)
+ return;
+
+ appIdStatsCloseFiles();
+}
+
+void appIdStatsIdleFlush()
+{
+ if (!enableAppStats)
+ return;
+
+ time_t now = get_time();
+ 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 StatsBucket* getStatsBucket(time_t startTime)
+{
+ StatsBucket* bucket = nullptr;
+
+ if ( !currBuckets )
+ {
+ currBuckets = sflist_new();
+# ifdef DEBUG_STATS
+ fprintf(SF_DEBUG_FILE, "New Stats Bucket List\n");
+# endif
+ }
+
+ if ( !currBuckets )
+ return nullptr;
+
+ SF_LNODE* lNode = nullptr;
+ StatsBucket* lBucket = nullptr;
+
+ for ( lBucket = (StatsBucket*)sflist_first(currBuckets, &lNode); lNode && lBucket;
+ lBucket = (StatsBucket*)sflist_next(&lNode) )
+ {
+ if (startTime == lBucket->startTime)
+ {
+ bucket = lBucket;
+ break;
+ }
+ else if (startTime < lBucket->startTime)
+ {
+ bucket = (StatsBucket*)snort_calloc(sizeof(StatsBucket));
+ 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 )
+ {
+ bucket = (StatsBucket*)snort_calloc(sizeof(StatsBucket));
+ 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()
+{
+ struct StatsBucket* bucket = nullptr;
+ uint8_t* buffer;
+ uint32_t* buffPtr;
+ struct FwAvlNode* node;
+ struct AppIdStatRecord* record;
+ size_t buffSize;
+ time_t currTime = time(nullptr);
+
+ if (logBuckets == nullptr)
+ return;
+
+ while ((bucket = (struct StatsBucket*)sflist_remove_head(logBuckets)) != nullptr)
+ {
+ 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 = (uint8_t*)snort_calloc(buffSize);
+# ifdef DEBUG_STATS
+ fprintf(SF_DEBUG_FILE, "Write App Records %u Size: %lu\n",
+ bucket->appRecordCnt, buffSize);
+# endif
+ }
+ else
+ buffer = nullptr;
+
+ 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 != nullptr; node = fwAvlNext(node))
+ {
+ struct AppIdStatOutputRecord* recBuffPtr;
+ const char* appName;
+ bool cooked_client = false;
+ AppId 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, pAppidActiveConfig);
+ 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
+ {
+ ErrorMessage("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
+ {
+ ErrorMessage(
+ "NGFW Rule Engine Failed to write to statistics file (%s): %s\n",
+ appFilePath, strerror(errno));
+ fclose(appfp);
+ appfp = nullptr;
+ }
+ }
+ }
+ snort_free(buffer);
+ }
+ fwAvlDeleteTree(bucket->appsTree, deleteRecord);
+ snort_free(bucket);
+ }
+}
+
+void appIdStatsFini()
+{
+ if (!enableAppStats)
+ return;
+
+ /*flush the last stats period. */
+ endStats2Period();
+ dumpStats2();
+
+ if (!currBuckets)
+ return;
+
+ while (auto bucket = (StatsBucket*)sflist_remove_head(currBuckets))
+ {
+ fwAvlDeleteTree(bucket->appsTree, deleteRecord);
+ snort_free(bucket);
+ }
+
+ snort_free(currBuckets);
+
+ if (logBuckets)
+ snort_free(logBuckets);
+
+ appIdStatsCloseFiles();
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// appid_stats.h author Sourcefire Inc.
+
+#ifndef APPID_STATS_H
+#define APPID_STATS_H
+
+class AppIdData;
+class AppIdModuleConfig;
+
+void appIdStatsUpdate(AppIdData*);
+void appIdStatsInit(AppIdModuleConfig* config);
+void appIdStatsReinit();
+void appIdStatsIdleFlush();
+void appIdStatsFini();
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// application_ids.h author Sourcefire Inc.
+
+#ifndef APPLICATION_IDS_H
+#define APPLICATION_IDS_H
+
+#include <cstdint>
+
+enum ApplicationId : int32_t
+{
+ 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_ICMP = 219,
+ APP_ID_ICMPV6 = 220,
+ 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_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_FTP_ACTIVE = 4002,
+ APP_ID_FTP_PASSIVE = 4003,
+
+ // FIXIT - L Need better name since often there is no UI
+ APP_ID_UNKNOWN_UI = 65535 // this causes the UI to render Unknown instead of pending or blank
+};
+
+struct AppRegistryEntry
+{
+ ApplicationId appId;
+ uint32_t additionalInfo;
+};
+
+#endif
--- /dev/null
+set ( CLIENT_PLUGINS_SOURCES
+ client_app_aim.cc
+ client_app_aim.h
+ client_app_api.h
+ client_app_base.cc
+ client_app_base.h
+ client_app_bit.cc
+ client_app_bit_tracker.cc
+ client_app_config.h
+ client_app_msn.cc
+ client_app_msn.h
+ client_app_rtp.cc
+ client_app_smtp.cc
+ client_app_smtp.h
+ client_app_ssh.cc
+ client_app_timbuktu.cc
+ client_app_tns.cc
+ client_app_vnc.cc
+ client_app_ym.cc
+ client_app_ym.h
+ )
+
+add_library ( appid_client_plugins STATIC
+ ${CLIENT_PLUGINS_SOURCES}
+)
+
+target_include_directories ( appid_client_plugins PRIVATE ${APPID_INCLUDE_DIR} )
+
+# FIXIT-H: Add unit tests
+
+#install (FILES ${CLIENT_PLUGINS_INCLUDES}
+# DESTINATION "${INCLUDE_INSTALL_PATH}/appid/client_plugins"
+
+#)
--- /dev/null
+
+AM_CPPFLAGS+=-I$(top_srcdir)/src/network_inspectors/appid
+
+file_list = \
+client_app_aim.cc \
+client_app_aim.h \
+client_app_api.h \
+client_app_base.cc \
+client_app_base.h \
+client_app_bit.cc \
+client_app_bit_tracker.cc \
+client_app_config.h \
+client_app_msn.cc \
+client_app_msn.h \
+client_app_rtp.cc \
+client_app_smtp.cc \
+client_app_smtp.h \
+client_app_ssh.cc \
+client_app_timbuktu.cc \
+client_app_tns.cc \
+client_app_vnc.cc \
+client_app_ym.cc \
+client_app_ym.h
+
+noinst_LIBRARIES = libappid_client_plugins.a
+libappid_client_plugins_a_SOURCES = $(file_list)
+
+# Uncomment once tests are ready.
+#if BUILD_UNIT_TESTS
+#SUBDIRS = test
+#endif
+
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// client_app_aim.cc author Sourcefire Inc.
+
+#include "client_app_api.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <cstdint>
+
+#include "main/snort_debug.h"
+#include "utils/sflsq.h"
+
+#include "app_info_table.h"
+#include "application_ids.h"
+
+#pragma pack(1)
+
+struct FLAPFNACSignOn
+{
+ uint16_t len;
+};
+
+struct FLAPFNAC
+{
+ uint16_t family;
+ uint16_t subtype;
+ uint16_t flags;
+ uint32_t id;
+};
+
+struct FLAPTLV
+{
+ uint16_t subtype;
+ uint16_t len;
+};
+
+struct FLAPHeader
+{
+ uint8_t start;
+ uint8_t channel;
+ uint16_t seq;
+ uint16_t len;
+};
+
+#pragma pack()
+
+struct AIM_CLIENT_APP_CONFIG
+{
+ int enabled;
+};
+
+THREAD_LOCAL AIM_CLIENT_APP_CONFIG aim_config;
+
+#define MAX_VERSION_SIZE 64
+
+static CLIENT_APP_RETCODE aim_init(const IniClientAppAPI* const, SF_LIST* config);
+static CLIENT_APP_RETCODE aim_validate(
+ const uint8_t* data, uint16_t size, const int dir, AppIdData*, Packet*,
+ Detector*, const AppIdConfig*);
+
+RNAClientAppModule aim_client_mod =
+{
+ "AIM", // name
+ IpProtocol::TCP, // proto
+ &aim_init, // init
+ nullptr, // clean
+ &aim_validate, // validate
+ 2, // minimum_matches
+ nullptr, // api
+ nullptr, // userData
+ 0, // precedence
+ nullptr, // finalize,
+ 1, // provides_user
+ 0 // flow_data_index
+};
+
+struct Client_App_Pattern
+{
+ const uint8_t* pattern;
+ unsigned length;
+ int index;
+ unsigned appId;
+};
+
+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, 0 },
+ { AIM_PROTOCOL_VERSION, sizeof(AIM_PROTOCOL_VERSION)-1, 4, 0 },
+ { 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 AppRegistryEntry 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 IniClientAppAPI* const init_api, SF_LIST* config)
+{
+ aim_config.enabled = 1;
+
+ if ( config )
+ {
+ SF_LNODE* cursor = nullptr;
+ RNAClientAppModuleConfigItem* item = nullptr;
+
+ for ( item = (RNAClientAppModuleConfigItem*)sflist_first(config, &cursor);
+ item;
+ item = (RNAClientAppModuleConfigItem*)sflist_next(&cursor) )
+ {
+ DebugFormat(DEBUG_INSPECTOR, "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 (unsigned i = 0; i < sizeof(patterns)/sizeof(*patterns); i++)
+ {
+ DebugFormat(DEBUG_INSPECTOR, "registering pattern length %u at %d\n",
+ patterns[i].length, patterns[i].index);
+
+ init_api->RegisterPattern(&aim_validate, IpProtocol::TCP, patterns[i].pattern,
+ patterns[i].length, patterns[i].index, init_api->pAppidConfig);
+ }
+ }
+
+ for (unsigned j = 0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++)
+ {
+ DebugFormat(DEBUG_INSPECTOR, "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;
+}
+
+template<typename Hdr>
+static inline const Hdr* advance(const uint8_t*& cur, const uint8_t* const end)
+{
+ assert(end >= cur);
+ if ( (size_t)(end - cur) < sizeof(Hdr) )
+ return nullptr;
+
+ cur += sizeof(Hdr);
+ return reinterpret_cast<const Hdr*>(cur);
+}
+
+static inline bool check_username(
+ const uint8_t* const data, const FLAPTLV* tlv, char* const buf, char* const buf_end)
+{
+ const uint8_t* const end = data + tlv->len;
+ char* ptr = buf;
+ *buf_end = '\0';
+
+ for ( const uint8_t* cur = data; cur < end; ++cur )
+ {
+ if (isalnum(*cur) || *cur == '.' || *cur == '@' || *cur == '-' || *cur == '_')
+ {
+ if ( ptr < buf_end )
+ *ptr++ = *cur;
+ }
+ else
+ return false;
+ }
+
+ return true;
+}
+
+static CLIENT_APP_RETCODE aim_validate(
+ const uint8_t* const data, uint16_t size, const int dir, AppIdData* flowp,
+ Packet*, Detector*, const AppIdConfig*)
+{
+ if ( dir != APP_ID_FROM_INITIATOR )
+ return CLIENT_APP_INPROCESS;
+
+ const uint8_t* const end = data + size;
+ const uint8_t* cur = data;
+
+ while ( cur < end )
+ {
+ auto fh = advance<FLAPHeader>(cur, end);
+ if ( !fh )
+ goto bail;
+
+ if (fh->start != 0x2a || fh->channel < 1 || fh->channel > 5)
+ goto bail;
+
+ uint16_t len = ntohs(fh->len);
+
+ if (len > (end - cur))
+ goto bail;
+
+ bool check_user_name = false;
+
+ if ( fh->channel == 0x02 )
+ {
+ auto fnac = advance<FLAPFNAC>(cur, end);
+ if ( !fnac )
+ goto bail;
+
+ if (fnac->family == htons(0x0017) && fnac->subtype == htons(0x0006))
+ check_user_name = true;
+
+ len -= sizeof(*fnac);
+ }
+ else if ( fh->channel == 0x01 )
+ {
+ if ( len < 4 || memcmp(cur, &AIM_PROTOCOL_VERSION[2], 4) != 0 )
+ goto bail;
+
+ len -= 4;
+ cur += 4;
+ }
+
+ if ( len )
+ {
+ bool got_id = false;
+ uint16_t major = 0;
+ uint16_t minor = 0;
+ uint16_t lesser = 0;
+
+ const uint8_t* const frame_end = cur + len;
+
+ while ( cur < frame_end )
+ {
+ auto tlv = advance<FLAPTLV>(cur, frame_end);
+ if ( !tlv )
+ goto bail;
+
+ if (frame_end - cur < tlv->len)
+ goto bail;
+
+ switch ( ntohs(tlv->subtype) )
+ {
+ case 0x0001:
+ if ( check_user_name )
+ {
+ constexpr auto USERNAME_LEN = 256;
+ char username[USERNAME_LEN];
+
+ if ( check_username(cur, tlv, username, username + USERNAME_LEN) )
+ aim_client_mod.api->add_user(flowp, username,
+ APP_ID_AOL_INSTANT_MESSENGER, 1);
+ }
+ break;
+ case 0x0003:
+ got_id = true;
+ break;
+ case 0x0017:
+ got_id = true;
+ major = ntohs(*(uint16_t*)cur);
+ break;
+ case 0x0018:
+ got_id = true;
+ minor = ntohs(*(uint16_t*)cur);
+ break;
+ case 0x0019:
+ got_id = true;
+ lesser = ntohs(*(uint16_t*)cur);
+ break;
+ default:
+ break;
+ }
+
+ cur += tlv->len;
+ }
+
+ if ( got_id )
+ {
+ char version[MAX_VERSION_SIZE];
+
+ snprintf(version, sizeof(version), "%d.%d.%d", major, minor, lesser);
+ aim_client_mod.api->add_app(
+ flowp, APP_ID_AOL_INSTANT_MESSENGER,
+ APP_ID_AOL_INSTANT_MESSENGER, version);
+ }
+ }
+ }
+
+ return CLIENT_APP_INPROCESS;
+
+bail:
+ setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED);
+ return CLIENT_APP_SUCCESS;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// client_app_aim.h author Sourcefire Inc.
+
+#ifndef CLIENT_APP_AIM_H
+#define CLIENT_APP_AIM_H
+
+#include "client_app_api.h"
+
+extern RNAClientAppModule aim_client_mod;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// client_app_api.h author Sourcefire Inc.
+
+#ifndef CLIENT_APP_API_H
+#define CLIENT_APP_API_H
+
+#include <cstdint>
+
+#include "appid_api.h"
+#include "appid_flow_data.h"
+
+struct Packet;
+struct Detector;
+
+// Forward declaration for AppId config. Cannot include appIdConfig.h because of
+// circular dependency
+class AppIdConfig;
+
+enum CLIENT_APP_RETCODE
+{
+ CLIENT_APP_SUCCESS = 0,
+ CLIENT_APP_INPROCESS = 10,
+ CLIENT_APP_ENULL = -10,
+ CLIENT_APP_EINVALID = -11,
+ CLIENT_APP_ENOMEM = -12
+};
+
+struct RNAClientAppModuleConfig
+{
+ const char* name;
+ SF_LIST items;
+};
+
+struct RNAClientAppModuleConfigItem
+{
+ const char* name;
+ const char* value;
+};
+
+using RNAClientAppFCN = CLIENT_APP_RETCODE(*)(
+ const uint8_t* data,
+ uint16_t size,
+ const int dir,
+ AppIdData*,
+ Packet*,
+ Detector*,
+ const AppIdConfig*
+);
+
+struct IniClientAppAPI
+{
+ void (* RegisterPattern)(
+ RNAClientAppFCN, IpProtocol proto, const uint8_t* const pattern,
+ unsigned size, int position, AppIdConfig*);
+
+ void (* RegisterPatternEx)(
+ RNAClientAppFCN, IpProtocol proto, const uint8_t* const pattern,
+ unsigned size, int position, Detector*);
+
+ void (* RegisterPatternNoCase)(
+ RNAClientAppFCN, IpProtocol proto, const uint8_t* const pattern,
+ unsigned size, int position, AppIdConfig*);
+
+ void (* RegisterAppId)(
+ RNAClientAppFCN, AppId, uint32_t additionalInfo, AppIdConfig*);
+
+ int debug;
+ uint32_t instance_id;
+ AppIdConfig* pAppidConfig; ///< AppId context for which this API should be used
+};
+
+struct CleanClientAppAPI
+{
+ AppIdConfig* pAppidConfig = nullptr; ///< AppId context for which this API should be used
+};
+
+struct FinalizeClientAppAPI
+{
+ void* data = nullptr;
+};
+
+using RNAClientAppInitFCN = CLIENT_APP_RETCODE(*)(const IniClientAppAPI* const, SF_LIST* config);
+using RNAClientAppFinalizeFCN = CLIENT_APP_RETCODE (*)(const FinalizeClientAppAPI* const);
+using RNAClientAppCleanFCN = void(*)(const CleanClientAppAPI* const);
+
+using ClientAppFlowdataGet = void*(*)(AppIdData*, unsigned);
+using ClientAppFlowdataAdd = int(*)(AppIdData*, void*, unsigned, AppIdFreeFCN);
+using ClientAppAddApp = void(*)(AppIdData*, AppId, AppId, const char*);
+using ClientAppAddInfo = void(*)(AppIdData*, const char*);
+using ClientAppAddUser = void(*)(AppIdData*, const char*, AppId, int);
+using ClientAppAddPayload = void(*)(AppIdData*, AppId);
+
+struct ClientAppApi
+{
+ ClientAppFlowdataGet data_get;
+ ClientAppFlowdataAdd data_add;
+ ClientAppAddApp add_app;
+ ClientAppAddInfo add_info;
+ ClientAppAddUser add_user;
+ ClientAppAddPayload add_payload;
+};
+
+struct RNAClientAppRecord
+{
+ RNAClientAppRecord* next;
+ const struct RNAClientAppModule* module;
+};
+
+struct RNAClientAppModule
+{
+ const char* name;
+ IpProtocol proto;
+ RNAClientAppInitFCN init;
+ RNAClientAppCleanFCN clean;
+ RNAClientAppFCN validate;
+ unsigned minimum_matches;
+ const ClientAppApi* api;
+ Detector* userData;
+
+ /**precedence of this detector.*/
+ unsigned int precedence;
+ RNAClientAppFinalizeFCN finalize;
+
+ int provides_user;
+ unsigned flow_data_index;
+};
+
+struct RNAClientAppFlowState
+{
+ RNAClientAppFlowState* next;
+ const RNAClientAppModule* ca;
+};
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// client_app_base.cc author Ron Dempster <Ron.Dempster@sourcefire.com>
+
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "client_app_base.h"
+
+#include "main/snort_debug.h"
+#include "log/messages.h"
+#include "protocols/packet.h"
+#include "utils/sflsq.h"
+#include "utils/util.h"
+#include "profiler/profiler.h"
+
+#include "appid_api.h"
+#include "appid_config.h"
+#include "fw_appid.h"
+#include "client_app_api.h"
+#include "client_app_base.h"
+#include "client_app_smtp.h"
+#include "client_app_msn.h"
+#include "client_app_aim.h"
+#include "client_app_ym.h"
+#include "detector_plugins/detector_sip.h"
+#include "lua_detector_module.h"
+#include "lua_detector_api.h"
+#include "http_common.h"
+#include "service_plugins/service_ssl.h"
+#include "detector_plugins/detector_dns.h"
+#include "detector_plugins/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(AppIdData* flowp, unsigned client_id);
+static int client_app_flowdata_add(AppIdData* flowp, void* data, unsigned client_id, AppIdFreeFCN
+ fcn);
+static void AppIdAddClientAppInfo(AppIdData* flowp, const char* info);
+
+static const ClientAppApi client_app_api =
+{
+ &client_app_flowdata_get,
+ &client_app_flowdata_add,
+ &AppIdAddClientApp,
+ &AppIdAddClientAppInfo,
+ &AppIdAddUser,
+ &AppIdAddPayload
+};
+
+static void LuaClientAppRegisterPattern(RNAClientAppFCN fcn, IpProtocol proto,
+ const uint8_t* const pattern, unsigned size,
+ int position, struct Detector* userData);
+static void CClientAppRegisterPattern(RNAClientAppFCN fcn, IpProtocol proto,
+ const uint8_t* const pattern, unsigned size,
+ int position, AppIdConfig* pConfig);
+static void CClientAppRegisterPatternNoCase(RNAClientAppFCN fcn, IpProtocol proto,
+ const uint8_t* const pattern, unsigned size,
+ int position, AppIdConfig* pConfig);
+
+static IniClientAppAPI client_init_api =
+{
+ &CClientAppRegisterPattern,
+ &LuaClientAppRegisterPattern,
+ &CClientAppRegisterPatternNoCase,
+ &appSetClientValidator,
+ 0,
+ 0,
+ nullptr
+};
+
+static CleanClientAppAPI clean_api =
+{
+};
+
+static FinalizeClientAppAPI finalize_api =
+{
+};
+
+extern RNAClientAppModule bit_client_mod;
+extern RNAClientAppModule bit_tracker_client_mod;
+extern RNAClientAppModule rtp_client_mod;
+extern RNAClientAppModule ssh_client_mod;
+extern RNAClientAppModule timbuktu_client_mod;
+extern RNAClientAppModule tns_client_mod;
+extern RNAClientAppModule vnc_client_mod;
+extern RNAClientAppModule pattern_udp_client_mod;
+extern RNAClientAppModule pattern_tcp_client_mod;
+extern RNAClientAppModule http_client_mod;
+
+static RNAClientAppModule* static_client_list[] =
+{
+ &smtp_client_mod,
+
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ &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,
+#endif
+ &dns_udp_client_mod,
+ &dns_tcp_client_mod,
+ &http_client_mod
+};
+
+/*static const char * const MODULE_NAME = "ClientApp"; */
+
+const ClientAppApi* getClientApi(void)
+{
+ return &client_app_api;
+}
+
+RNAClientAppModuleConfig* geClientAppModuleConfig(const char* moduleName,
+ ClientAppConfig* pClientAppConfig)
+{
+ SF_LNODE* cursor;
+ RNAClientAppModuleConfig* mod_config;
+
+ for (mod_config = (RNAClientAppModuleConfig*)sflist_first(&pClientAppConfig->module_configs,
+ &cursor);
+ mod_config;
+ mod_config = (RNAClientAppModuleConfig*)sflist_next(&cursor))
+ {
+ if (strcasecmp(mod_config->name, moduleName) == 0)
+ break;
+ }
+ return mod_config;
+}
+
+const RNAClientAppModule* ClientAppGetClientAppModule(RNAClientAppFCN fcn, struct
+ Detector* userdata,
+ ClientAppConfig* 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 nullptr;
+}
+
+void add_pattern_data(SearchTool* st, const RNAClientAppModule* li, int position, const
+ uint8_t* const pattern, unsigned size,
+ unsigned nocase, int* count, ClientAppConfig* pClientAppConfig)
+{
+ ClientPatternData* pd = (ClientPatternData*)snort_calloc(sizeof(ClientPatternData));
+ pd->ca = li;
+ pd->position = position;
+ (*count)++;
+ pd->next = pClientAppConfig->pattern_data_list;
+ pClientAppConfig->pattern_data_list = pd;
+ st->add((const char*)pattern, size, pd, nocase);
+}
+
+static void clientCreatePattern(IpProtocol proto, const uint8_t* const pattern, unsigned size,
+ int position, unsigned nocase, const RNAClientAppModule* li, ClientAppConfig* pClientAppConfig)
+{
+ int* count;
+
+ if (!li)
+ {
+ ErrorMessage("Invalid client app when registering a pattern");
+ return;
+ }
+
+ if (proto == IpProtocol::TCP)
+ {
+ if (pClientAppConfig->tcp_patterns)
+ delete pClientAppConfig->tcp_patterns;
+ pClientAppConfig->tcp_patterns = new SearchTool("ac_full");
+ pClientAppConfig->udp_patterns = nullptr;
+ count = &pClientAppConfig->tcp_pattern_count;
+ add_pattern_data(pClientAppConfig->tcp_patterns, li, position, pattern, size,
+ nocase, count, pClientAppConfig);
+ }
+ else if (proto == IpProtocol::UDP)
+ {
+ if (pClientAppConfig->udp_patterns)
+ delete pClientAppConfig->udp_patterns;
+ pClientAppConfig->udp_patterns = new SearchTool("ac_full");
+ pClientAppConfig->tcp_patterns = nullptr;
+ count = &pClientAppConfig->udp_pattern_count;
+ add_pattern_data(pClientAppConfig->udp_patterns, li, position, pattern, size,
+ nocase, count, pClientAppConfig);
+ }
+ else
+ {
+ pClientAppConfig->tcp_patterns = nullptr;
+ pClientAppConfig->udp_patterns = nullptr;
+ ErrorMessage("Invalid protocol when registering a pattern: %u\n",(unsigned)proto);
+ }
+}
+
+static void CClientAppRegisterPattern(RNAClientAppFCN fcn, IpProtocol proto,
+ const uint8_t* const pattern, unsigned size,
+ int position, AppIdConfig* pConfig)
+{
+ ClientAppRegisterPattern(fcn, proto, pattern, size, position, 0, nullptr,
+ &pConfig->clientAppConfig);
+}
+
+static void CClientAppRegisterPatternNoCase(RNAClientAppFCN fcn, IpProtocol proto,
+ const uint8_t* const pattern, unsigned size,
+ int position, AppIdConfig* pConfig)
+{
+ ClientAppRegisterPattern(fcn, proto, pattern, size, position, 1, nullptr,
+ &pConfig->clientAppConfig);
+}
+
+static void LuaClientAppRegisterPattern(RNAClientAppFCN fcn, IpProtocol 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, IpProtocol proto, const uint8_t* const pattern,
+ unsigned size, int position, unsigned nocase, struct Detector* userData,
+ ClientAppConfig* pClientAppConfig)
+{
+ RNAClientAppRecord* list;
+ RNAClientAppRecord* li;
+
+ if (proto == IpProtocol::TCP)
+ list = pClientAppConfig->tcp_client_app_list;
+ else if (proto == IpProtocol::UDP)
+ list = pClientAppConfig->udp_client_app_list;
+ else
+ {
+ ErrorMessage("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(proto, pattern, size, position, nocase, li->module,
+ pClientAppConfig);
+ break;
+ }
+ }
+}
+
+int ClientAppLoadForConfigCallback(void* symbol, ClientAppConfig* pClientAppConfig)
+{
+ static unsigned client_module_index = 0;
+ RNAClientAppModule* cam = (RNAClientAppModule*)symbol;
+ RNAClientAppRecord** list = nullptr;
+ RNAClientAppRecord* li;
+
+ DebugFormat(DEBUG_LOG,"Adding client %s for protocol %u\n",cam->name, (unsigned)cam->proto);
+
+ if (client_module_index >= 65536)
+ {
+ ErrorMessage("Maximum number of client modules exceeded");
+ return -1;
+ }
+
+ if (cam->proto == IpProtocol::TCP)
+ {
+ list = &pClientAppConfig->tcp_client_app_list;
+ }
+ else if (cam->proto == IpProtocol::UDP)
+ {
+ list = &pClientAppConfig->udp_client_app_list;
+ }
+ else
+ {
+ ErrorMessage("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)
+ {
+ li = (RNAClientAppRecord*)snort_calloc(sizeof(RNAClientAppRecord));
+ 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 nullptr because Lua detectors use it although C detectors don't
+ cam->userData = nullptr; */
+ return 0;
+}
+
+int ClientAppLoadCallback(void* symbol)
+{
+ return ClientAppLoadForConfigCallback(symbol, &pAppidActiveConfig->clientAppConfig);
+}
+
+int LoadClientAppModules(AppIdConfig* pConfig)
+{
+ unsigned i;
+
+ for (i=0; i<sizeof(static_client_list)/sizeof(*static_client_list); i++)
+ {
+ if (ClientAppLoadForConfigCallback(static_client_list[i], &pConfig->clientAppConfig))
+ return -1;
+ }
+
+ return 0;
+}
+
+static void AddModuleConfigItem(char* module_name, char* item_name, char* item_value,
+ ClientAppConfig* config)
+{
+ SF_LNODE* cursor;
+ RNAClientAppModuleConfig* mod_config;
+ RNAClientAppModuleConfigItem* item;
+
+ for (mod_config = (RNAClientAppModuleConfig*)sflist_first(&config->module_configs, &cursor);
+ mod_config;
+ mod_config = (RNAClientAppModuleConfig*)sflist_next(&cursor))
+ {
+ if (strcasecmp(mod_config->name, module_name) == 0)
+ break;
+ }
+
+ if (!mod_config)
+ {
+ mod_config = (RNAClientAppModuleConfig*)snort_calloc(sizeof(RNAClientAppModuleConfig));
+ mod_config->name = snort_strdup(module_name);
+ sflist_init(&mod_config->items);
+ if (sflist_add_tail(&config->module_configs, mod_config))
+ {
+ ErrorMessage("Failed to add a module configuration");
+ exit(-1);
+ }
+ }
+
+ for (item = (RNAClientAppModuleConfigItem*)sflist_first(&mod_config->items, &cursor);
+ item;
+ item = (RNAClientAppModuleConfigItem*)sflist_next(&cursor))
+ {
+ if (strcasecmp(item->name, item_name) == 0)
+ break;
+ }
+
+ if (!item)
+ {
+ item = (RNAClientAppModuleConfigItem*)snort_calloc(sizeof(RNAClientAppModuleConfigItem));
+ item->name = snort_strdup(item_name);
+ if (sflist_add_tail(&mod_config->items, item))
+ {
+ ErrorMessage("Failed to add a module configuration item");
+ exit(-1);
+ }
+ }
+
+ if (item->value)
+ snort_free((void*)item->value);
+
+ item->value = snort_strdup(item_value);
+}
+
+static void ClientAppParseOption(ClientAppConfig* 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
+ {
+ DebugFormat(DEBUG_LOG, "Unknown client app argument ignored: key(%s) value(%s)",
+ key, value);
+ }
+}
+
+static int ClientAppParseArgs(ClientAppConfig* config, SF_LIST* args)
+{
+ ConfigItem* ci;
+ SF_LNODE* cursor;
+
+ for (ci=(ConfigItem*)sflist_first(args, &cursor);
+ ci;
+ ci=(ConfigItem*)sflist_next(&cursor))
+ {
+ ClientAppParseOption(config, ci->name, ci->value);
+ }
+
+ return 0;
+}
+
+#define MAX_DISPLAY_SIZE 65536
+static void DisplayClientAppConfig(ClientAppConfig* config)
+{
+ static char buffer[MAX_DISPLAY_SIZE];
+ int position = 0;
+ int tmp;
+ RNAClientAppModuleConfig* mod_config;
+ RNAClientAppModuleConfigItem* item;
+ SF_LNODE* cursor;
+
+ 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, &cursor);
+ mod_config;
+ mod_config = (RNAClientAppModuleConfig*)sflist_next(&cursor))
+ {
+ SF_LNODE* cursor;
+
+ 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, &cursor);
+ item;
+ item = (RNAClientAppModuleConfigItem*)sflist_next(&cursor))
+ {
+ 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;
+
+ DebugFormat(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)
+ snort_free((void*)item->name);
+ if (item->value)
+ snort_free((void*)item->value);
+ snort_free(item);
+ }
+}
+
+static void free_module_config(void* module_config)
+{
+ RNAClientAppModuleConfig* config = (RNAClientAppModuleConfig*)module_config;
+ if (config)
+ {
+ if (config->name)
+ snort_free((void*)config->name);
+ sflist_static_free_all(&config->items, &free_module_config_item);
+ snort_free(config);
+ }
+}
+
+static void initialize_module(RNAClientAppRecord* li, ClientAppConfig* pClientAppConfig)
+{
+ RNAClientAppModuleConfig* mod_config;
+ SF_LNODE* cursor;
+ int rval;
+
+ for (mod_config = (RNAClientAppModuleConfig*)sflist_first(&pClientAppConfig->module_configs,
+ &cursor);
+ mod_config;
+ mod_config = (RNAClientAppModuleConfig*)sflist_next(&cursor))
+ {
+ 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 : nullptr)) != CLIENT_APP_SUCCESS)
+ {
+ ErrorMessage("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 )
+ {
+ ErrorMessage("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(AppIdConfig* pConfig)
+{
+ ClientPatternData* 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);
+
+ // FIXIT - should this be deleted here? or move this clean up to a dtor?
+ delete pConfig->clientAppConfig.tcp_patterns;
+ pConfig->clientAppConfig.tcp_patterns = nullptr;
+
+ delete pConfig->clientAppConfig.udp_patterns;
+ pConfig->clientAppConfig.udp_patterns = nullptr;
+
+ while (pConfig->clientAppConfig.pattern_data_list)
+ {
+ pd = pConfig->clientAppConfig.pattern_data_list;
+ pConfig->clientAppConfig.pattern_data_list = pd->next;
+ snort_free((void*)pd);
+ }
+
+ CleanHttpPatternLists(pConfig);
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ ssl_detector_free_patterns(&pConfig->serviceSslConfig);
+#endif
+ 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(AppIdConfig* 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;
+ // FIXIT - active config global must go...
+ client_init_api.instance_id = pAppidActiveConfig->mod_config->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(AppIdConfig* pConfig)
+{
+ if (pConfig->clientAppConfig.enabled)
+ {
+ if ( pConfig->clientAppConfig.tcp_patterns )
+ pConfig->clientAppConfig.tcp_patterns->prep();
+
+ if ( pConfig->clientAppConfig.udp_patterns )
+ pConfig->clientAppConfig.udp_patterns->prep();
+ }
+}
+
+struct ClientAppMatch
+{
+ struct ClientAppMatch* next;
+ unsigned count;
+ const RNAClientAppModule* ca;
+};
+
+static ClientAppMatch* match_free_list;
+
+/**
+ * Clean up the configuration of the client app module
+ */
+void CleanupClientApp(AppIdConfig* /* pConfig */)
+{
+#ifdef RNA_FULL_CLEANUP
+ ClientAppMatch* match;
+ ClientPatternData* pd;
+ RNAClientAppRecord* li;
+
+ clean_api.pAppidConfig = pConfig;
+ if (pConfig->clientAppConfig.tcp_patterns)
+ {
+ delete pConfig->clientAppConfig.tcp_patterns;
+ pConfig->clientAppConfig.tcp_patterns = nullptr;
+ }
+ if (pConfig->clientAppConfig.udp_patterns)
+ {
+ delete pConfig->clientAppConfig.udp_patterns;
+ pConfig->clientAppConfig.udp_patterns = nullptr;
+ }
+ while ((pd = pConfig->clientAppConfig.pattern_data_list) != nullptr)
+ {
+ pConfig->clientAppConfig.pattern_data_list = pd->next;
+ snort_free(pd);
+ }
+
+ while ((li=pConfig->clientAppConfig.tcp_client_app_list) != nullptr)
+ {
+ pConfig->clientAppConfig.tcp_client_app_list = li->next;
+ if (li->module->clean)
+ li->module->clean(&clean_api);
+ snort_free(li);
+ }
+ while ((li=pConfig->clientAppConfig.udp_client_app_list) != nullptr)
+ {
+ pConfig->clientAppConfig.udp_client_app_list = li->next;
+ if (li->module->clean)
+ li->module->clean(&clean_api);
+ snort_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) != nullptr)
+ {
+ match_free_list = match->next;
+ snort_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;
+ ClientPatternData* pd = (ClientPatternData*)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 = (ClientAppMatch*)snort_calloc(sizeof(ClientAppMatch));
+
+ cam->count = 1;
+ cam->ca = pd->ca;
+ cam->next = *matches;
+ *matches = cam;
+ }
+ return 0;
+}
+
+void AppIdAddClientApp(AppIdData* flowp, AppId service_id, AppId id, const char* version)
+{
+ if (version)
+ {
+ if (flowp->clientVersion)
+ {
+ if (strcmp(version, flowp->clientVersion))
+ {
+ snort_free(flowp->clientVersion);
+ flowp->clientVersion = snort_strdup(version);
+ }
+ }
+ else
+ flowp->clientVersion = snort_strdup(version);
+ }
+
+ setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED);
+ flowp->ClientServiceAppId = service_id;
+ flowp->ClientAppId = id;
+ checkSandboxDetection(id);
+}
+
+static void AppIdAddClientAppInfo(AppIdData* flowp, const char* info)
+{
+ if (flowp->hsession && !flowp->hsession->url)
+ flowp->hsession->url = snort_strdup(info);
+}
+
+static ClientAppMatch* BuildClientPatternList(const Packet* pkt, IpProtocol protocol,
+ const ClientAppConfig* pClientAppConfig)
+{
+ ClientAppMatch* match_list = nullptr;
+ SearchTool* patterns;
+
+ if (protocol == IpProtocol::TCP)
+ patterns = pClientAppConfig->tcp_patterns;
+ else
+ patterns = pClientAppConfig->udp_patterns;
+
+ if (!patterns)
+ return nullptr;
+
+ patterns->find_all((char*)pkt->data, pkt->dsize, &pattern_match, false, (void*)&match_list);
+
+ return match_list;
+}
+
+static const RNAClientAppModule* GetNextFromClientPatternList(ClientAppMatch** match_list)
+{
+ ClientAppMatch* curr = nullptr;
+ ClientAppMatch* prev = nullptr;
+ ClientAppMatch* max_curr = nullptr;
+ ClientAppMatch* max_prev = nullptr;
+ 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 != nullptr)
+ {
+ if (max_prev == nullptr)
+ {
+ *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 nullptr;
+ }
+}
+
+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 = nullptr;
+}
+
+/**
+ * The process to determine the running client app given the packet data.
+ *
+ * @param p packet to process
+ */
+static void ClientAppID(Packet* p, const int /*direction*/, AppIdData* flowp, const
+ AppIdConfig* pConfig)
+{
+ const RNAClientAppModule* client = nullptr;
+ ClientAppMatch* match_list;
+
+#ifdef CLIENT_APP_DEBUG
+ _dpd.logMsg("Client");
+#endif
+
+ if (!p->dsize)
+ return;
+
+ if (flowp->clientData != nullptr)
+ return;
+
+ if (flowp->candidate_client_list != nullptr)
+ {
+ if (flowp->num_candidate_clients_tried > 0)
+ return;
+ }
+ else
+ {
+ flowp->candidate_client_list = (SF_LIST*) snort_calloc(sizeof(SF_LIST));
+ 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 RNAClientAppModule* tmp = GetNextFromClientPatternList(&match_list);
+ if (tmp != nullptr)
+ {
+ SF_LNODE* cursor;
+
+ client = (RNAClientAppModule*)sflist_first(flowp->candidate_client_list, &cursor);
+ while (client && (client != tmp))
+ client = (RNAClientAppModule*)sflist_next(&cursor);
+ if (client == nullptr)
+ {
+ 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);
+
+ if (sflist_count(flowp->candidate_client_list) == 0)
+ {
+ client = nullptr;
+ switch (p->ptrs.dp)
+ {
+ case 465:
+ if (getAppIdFlag(flowp, APPID_SESSION_DECRYPTED))
+ client = &smtp_client_mod;
+ break;
+ default:
+ break;
+ }
+ if (client != nullptr)
+ {
+ sflist_add_tail(flowp->candidate_client_list, (void*)client);
+ flowp->num_candidate_clients_tried++;
+ }
+ }
+}
+
+int AppIdDiscoverClientApp(Packet* p, int direction, AppIdData* rnaData, const
+ AppIdConfig* 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* geAppUrlList(AppIdConfig* pConfig)
+{
+ HttpPatternLists* patternLists = &pConfig->httpPatternLists;
+ return (&patternLists->appUrlList);
+}
+
+static void* client_app_flowdata_get(AppIdData* flowp, unsigned client_id)
+{
+ return AppIdFlowdataGet(flowp, client_id);
+}
+
+static int client_app_flowdata_add(AppIdData* flowp, void* data, unsigned client_id, AppIdFreeFCN
+ fcn)
+{
+ return AppIdFlowdataAdd(flowp, data, client_id, fcn);
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// client_app_base.h author Sourcefire Inc.
+
+#ifndef CLIENT_APP_BASE_H
+#define CLIENT_APP_BASE_H
+
+#include "appid_api.h"
+
+#include "client_app_api.h"
+
+#define GENERIC_APP_OFFSET 2000000000
+
+class AppIdData;
+class AppIdConfig;
+struct Detector;
+struct DetectorSipConfig;
+struct ClientAppConfig;
+struct RNAClientAppModule;
+struct DetectorAppUrlList;
+struct Packet;
+struct ClientAppApi;
+struct RNAClientAppModuleConfig;
+
+void ClientAppInit(AppIdConfig*);
+void ClientAppFinalize(AppIdConfig*);
+void UnconfigureClientApp(AppIdConfig*);
+void CleanupClientApp(AppIdConfig*);
+int ClientAppLoadCallback(void* symbol);
+int ClientAppLoadForConfigCallback(void* symbol, ClientAppConfig*);
+int LoadClientAppModules(AppIdConfig*);
+void ClientAppRegisterPattern(RNAClientAppFCN, IpProtocol proto, const uint8_t* const pattern,
+ unsigned size, int position, unsigned nocase, Detector*, ClientAppConfig*);
+const ClientAppApi* getClientApi();
+RNAClientAppModuleConfig* getClientAppModuleConfig(const char* moduleName, ClientAppConfig*);
+int AppIdDiscoverClientApp(Packet* p, int direction, AppIdData*, const AppIdConfig*);
+void AppIdAddClientApp(AppIdData*, AppId service_id, AppId id, const char* version);
+DetectorAppUrlList* getAppUrlList(AppIdConfig*);
+const RNAClientAppModule* ClientAppGetClientAppModule(RNAClientAppFCN, Detector*,
+ ClientAppConfig*);
+int sipUaPatternAdd( AppId, const char* clientVersion, const char* uaPattern, DetectorSipConfig*);
+int sipServerPatternAdd(AppId, const char* clientVersion, const char* uaPattern,
+ DetectorSipConfig*);
+int sipUaFinalize(DetectorSipConfig*);
+int sipServerFinalize();
+int portPatternFinalize(AppIdConfig*);
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// client_app_bit.cc author Sourcefire Inc.
+
+#include "client_app_api.h"
+#include "application_ids.h"
+
+#include "main/snort_debug.h"
+#include "utils/util.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)
+
+enum BITState
+{
+ BIT_STATE_BANNER = 0,
+ BIT_STATE_BANNER_DC,
+ BIT_STATE_MESSAGE_LEN,
+ BIT_STATE_MESSAGE_DATA
+};
+
+struct ClientBITData
+{
+ BITState state;
+ unsigned stringlen;
+ unsigned pos;
+ union
+ {
+ uint32_t len;
+ uint8_t raw_len[4];
+ } l;
+};
+
+#pragma pack(1)
+struct ClientBITMsg
+{
+ uint32_t len;
+ uint8_t code;
+};
+#pragma pack()
+
+struct BIT_CLIENT_APP_CONFIG
+{
+ int enabled;
+};
+
+THREAD_LOCAL BIT_CLIENT_APP_CONFIG bit_config;
+
+static CLIENT_APP_RETCODE bit_init(const IniClientAppAPI* const init_api, SF_LIST* config);
+static CLIENT_APP_RETCODE bit_validate(const uint8_t* data, uint16_t size, const int dir,
+ AppIdData* flowp, Packet* pkt, struct Detector* userData,
+ const AppIdConfig* pConfig);
+
+SO_PUBLIC RNAClientAppModule bit_client_mod =
+{
+ "BIT",
+ IpProtocol::TCP,
+ &bit_init,
+ nullptr,
+ &bit_validate,
+ 1,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ 0
+};
+
+struct Client_App_Pattern
+{
+ const u_int8_t* pattern;
+ unsigned length;
+ int index;
+ unsigned appId;
+};
+
+static Client_App_Pattern patterns[] =
+{
+ { (const uint8_t*)BIT_BANNER, sizeof(BIT_BANNER)-1, 0, APP_ID_BITTORRENT },
+};
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_BITTORRENT, 0 }
+};
+
+static CLIENT_APP_RETCODE bit_init(const IniClientAppAPI* const init_api, SF_LIST* config)
+{
+ unsigned i;
+ RNAClientAppModuleConfigItem* item;
+
+ bit_config.enabled = 1;
+
+ if (config)
+ {
+ SF_LNODE* cursor = nullptr;
+ for (item = (RNAClientAppModuleConfigItem*)sflist_first(config, &cursor);
+ item;
+ item = (RNAClientAppModuleConfigItem*)sflist_next(&cursor))
+ {
+ DebugFormat(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++)
+ {
+ DebugFormat(DEBUG_LOG,"registering patterns: %s: %d\n",
+ (const char*)patterns[i].pattern, patterns[i].index);
+ init_api->RegisterPattern(&bit_validate, IpProtocol::TCP, patterns[i].pattern,
+ patterns[i].length, patterns[i].index, init_api->pAppidConfig);
+ }
+ }
+
+ unsigned j;
+ for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++)
+ {
+ DebugFormat(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,
+ AppIdData* flowp, Packet*, struct Detector*, const AppIdConfig*)
+{
+ ClientBITData* fd;
+ uint16_t offset;
+
+ if (dir != APP_ID_FROM_INITIATOR)
+ return CLIENT_APP_INPROCESS;
+
+ fd = (ClientBITData*)bit_client_mod.api->data_get(flowp, bit_client_mod.flow_data_index);
+ if (!fd)
+ {
+ fd = (ClientBITData*)snort_calloc(sizeof(ClientBITData));
+ bit_client_mod.api->data_add(flowp, fd, bit_client_mod.flow_data_index, &snort_free);
+ 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(flowp, APP_ID_BITTORRENT, APP_ID_BITTORRENT, nullptr);
+ setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED);
+ return CLIENT_APP_SUCCESS;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// client_app_bit_tracker.cc author Sourcefire Inc.
+
+#include "main/snort_debug.h"
+#include "protocols/packet.h"
+#include "utils/sflsq.h"
+#include "utils/util.h"
+
+#include "app_info_table.h"
+#include "application_ids.h"
+#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)
+
+enum BITState
+{
+ BIT_STATE_BANNER = 0,
+ BIT_STATE_TYPES,
+ BIT_STATE_DC,
+ BIT_STATE_CHECK_END,
+ BIT_STATE_CHECK_END_TYPES,
+ BIT_STATE_CHECK_LAST
+};
+
+enum BITType
+{
+ BIT_TYPE_REQUEST = 1,
+ BIT_TYPE_RESPONSE,
+ BIT_TYPE_ERROR
+};
+
+struct ClientBITData
+{
+ BITState state;
+ BITType type;
+ unsigned pos;
+};
+
+struct BIT_CLIENT_APP_CONFIG
+{
+ int enabled;
+};
+
+THREAD_LOCAL BIT_CLIENT_APP_CONFIG udp_bit_config;
+
+static CLIENT_APP_RETCODE udp_bit_init(const IniClientAppAPI* const init_api, SF_LIST* config);
+static CLIENT_APP_RETCODE udp_bit_validate(const uint8_t* data, uint16_t size, const int dir,
+ AppIdData* flowp, Packet* pkt, struct Detector* userData, const AppIdConfig* pConfig);
+
+SO_PUBLIC RNAClientAppModule bit_tracker_client_mod =
+{
+ "BIT-UDP", // name
+ IpProtocol::UDP, // proto
+ &udp_bit_init, // init
+ nullptr, // clean
+ &udp_bit_validate, // validate
+ 1, // minimum_matches
+ nullptr, // api
+ nullptr, // userData
+ 0, // precedence
+ nullptr, // finalize,
+ 1, // provides_user
+ 0 // flow_data_index
+};
+
+struct Client_App_Pattern
+{
+ const u_int8_t* pattern;
+ unsigned length;
+ int index;
+ unsigned appId;
+};
+
+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 AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_BITTRACKER_CLIENT, 0 }
+};
+
+static CLIENT_APP_RETCODE udp_bit_init(const IniClientAppAPI* const init_api, SF_LIST* config)
+{
+ unsigned i;
+
+ udp_bit_config.enabled = 1;
+
+ if (config)
+ {
+ SF_LNODE* cursor;
+ RNAClientAppModuleConfigItem* item;
+
+ for (item = (RNAClientAppModuleConfigItem*)sflist_first(config, &cursor);
+ item;
+ item = (RNAClientAppModuleConfigItem*)sflist_next(&cursor))
+ {
+ DebugFormat(DEBUG_LOG,"Processing %s: %s\n",item->name, item->value);
+ if (strcasecmp(item->name, "enabled") == 0)
+ {
+ udp_bit_config.enabled = atoi(item->value);
+ }
+ }
+ }
+
+ if (udp_bit_config.enabled)
+ {
+ for (i=0; i < sizeof(udp_patterns)/sizeof(*udp_patterns); i++)
+ {
+ DebugFormat(DEBUG_LOG,"registering patterns: %s: %d\n",
+ (const char*)udp_patterns[i].pattern, udp_patterns[i].index);
+ init_api->RegisterPattern(&udp_bit_validate, IpProtocol::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++)
+ {
+ DebugFormat(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*/,
+ AppIdData* flowp, Packet*, struct Detector*, const AppIdConfig*)
+{
+ ClientBITData* fd;
+ uint16_t offset;
+
+ if (size < (UDP_BIT_FIRST_LEN + UDP_BIT_END_LEN + 3))
+ return CLIENT_APP_EINVALID;
+
+ fd = (ClientBITData*)bit_tracker_client_mod.api->data_get(flowp,
+ bit_tracker_client_mod.flow_data_index);
+ if (!fd)
+ {
+ fd = (ClientBITData*)snort_calloc(sizeof(ClientBITData));
+ bit_tracker_client_mod.api->data_add(flowp, fd,
+ bit_tracker_client_mod.flow_data_index, &snort_free);
+ 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(flowp, APP_ID_BITTORRENT, APP_ID_BITTRACKER_CLIENT,
+ nullptr);
+ setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED);
+ return CLIENT_APP_SUCCESS;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// client_app_config.h author Sourcefire Inc.
+
+#ifndef CLIENT_APP_CONFIG_H
+#define CLIENT_APP_CONFIG_H
+
+#include "utils/sflsq.h"
+#include "search_engines/search_tool.h"
+
+struct RNAClientAppModule;
+struct RNAClientAppRecord;
+
+struct ClientPatternData
+{
+ ClientPatternData* next;
+ int position;
+ const RNAClientAppModule* ca;
+};
+
+struct ClientAppConfig
+{
+ RNAClientAppRecord* tcp_client_app_list; // List of all TCP client apps (C and Lua)
+ RNAClientAppRecord* udp_client_app_list; // List of all UDP client apps (C and Lua)
+ int enabled;
+ SF_LIST module_configs;
+ ClientPatternData* pattern_data_list;
+ SearchTool* tcp_patterns;
+ int tcp_pattern_count;
+ SearchTool* udp_patterns;
+ int udp_pattern_count;
+};
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// client_app_msn.cc author Sourcefire Inc.
+
+#include "client_app_msn.h"
+#include "main/snort_debug.h"
+#include "app_info_table.h"
+#include "application_ids.h"
+
+#define MAX_VERSION_SIZE 64
+
+struct MSN_CLIENT_APP_CONFIG
+{
+ int enabled;
+};
+
+THREAD_LOCAL MSN_CLIENT_APP_CONFIG msn_config;
+
+static CLIENT_APP_RETCODE msn_validate(const uint8_t* data, uint16_t size, const int dir,
+ AppIdData* flowp, Packet* pkt, struct Detector* userData, const AppIdConfig* pConfig);
+
+struct Client_App_Pattern
+{
+ const uint8_t* pattern;
+ unsigned length;
+ int index;
+ unsigned appId;
+};
+
+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 AppRegistryEntry 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 IniClientAppAPI* const init_api, SF_LIST* config)
+{
+ RNAClientAppModuleConfigItem* item;
+ msn_config.enabled = 1;
+
+ if (config)
+ {
+ SF_LNODE* iter = nullptr;
+
+ for (item = (RNAClientAppModuleConfigItem*)sflist_first(config, &iter);
+ item;
+ item = (RNAClientAppModuleConfigItem*)sflist_next(&iter))
+ {
+ DebugFormat(DEBUG_APPID,"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 ( unsigned i=0; i < sizeof(patterns)/sizeof(*patterns); i++ )
+ {
+ DebugFormat(DEBUG_APPID,"registering patterns: %s: %d\n",
+ (const char*)patterns[i].pattern, patterns[i].index);
+ init_api->RegisterPattern(&msn_validate, IpProtocol::TCP, patterns[i].pattern,
+ patterns[i].length, patterns[i].index, init_api->pAppidConfig);
+ }
+ }
+
+ for ( unsigned j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++ )
+ {
+ DebugFormat(DEBUG_APPID,"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,
+ AppIdData* flowp, Packet* pkt, struct Detector*, const AppIdConfig*)
+{
+ 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(flowp, APP_ID_MSN_MESSENGER, product_id, (char*)version);
+ setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED);
+ return CLIENT_APP_SUCCESS;
+}
+
+RNAClientAppModule msn_client_mod =
+{
+ "MSN",
+ IpProtocol::TCP,
+ msn_init,
+ nullptr, // clean
+ msn_validate,
+ 2, // minimum_matches
+ nullptr, // api
+ nullptr, // userData
+ 0, // precedence
+ nullptr, // finalize
+ 0, // provides_user
+ 0 // flow_data_index
+};
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// client_app_msn.h author Sourcefire Inc.
+
+#ifndef CLIENT_APP_MSN_H
+#define CLIENT_APP_MSN_H
+
+#include "client_app_api.h"
+
+extern RNAClientAppModule msn_client_mod;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// client_app_rtp.cc author Sourcefire Inc.
+
+#include "main/snort_debug.h"
+#include "protocols/packet.h"
+#include "utils/sflsq.h"
+#include "utils/util.h"
+
+#include "application_ids.h"
+#include "client_app_api.h"
+
+enum RTPState
+{
+ RTP_STATE_CONNECTION,
+ RTP_STATE_CONTINUE
+};
+
+#define MAX_REMOTE_SIZE 128
+#define NUMBER_OF_PACKETS 3
+
+struct ClientRTPData
+{
+ RTPState state;
+ uint8_t pos;
+ uint16_t init_seq;
+ uint16_t resp_seq;
+ uint8_t init_count;
+ uint8_t resp_count;
+ uint32_t init_timestamp;
+ uint32_t resp_timestamp;
+ uint32_t init_ssrc;
+ uint32_t resp_ssrc;
+};
+
+#pragma pack(1)
+struct ClientRTPMsg
+{
+#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;
+};
+#pragma pack()
+
+struct RTP_CLIENT_APP_CONFIG
+{
+ int enabled;
+};
+
+THREAD_LOCAL RTP_CLIENT_APP_CONFIG rtp_config;
+
+static CLIENT_APP_RETCODE rtp_init(const IniClientAppAPI* const init_api, SF_LIST* config);
+static CLIENT_APP_RETCODE rtp_validate(const uint8_t* data, uint16_t size, const int dir,
+ AppIdData* flowp, Packet* pkt, struct Detector* userData, const AppIdConfig* pConfig);
+
+SO_PUBLIC RNAClientAppModule rtp_client_mod =
+{
+ "RTP", // name
+ IpProtocol::UDP, // proto
+ &rtp_init, // init
+ nullptr, // clean
+ &rtp_validate, // validate
+ 1, // minimum_matches
+ nullptr, // api
+ nullptr, // userData
+ 0, // precedence
+ nullptr, // finalize,
+ 1, // provides_user
+ 0 // flow_data_index
+};
+
+struct Client_App_Pattern
+{
+ const u_int8_t* pattern;
+ unsigned length;
+ int index;
+ unsigned appId;
+};
+
+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 AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_RTP, 0 }
+};
+
+static CLIENT_APP_RETCODE rtp_init(const IniClientAppAPI* const init_api, SF_LIST* config)
+{
+ unsigned i;
+
+ rtp_config.enabled = 1;
+
+ if (config)
+ {
+ SF_LNODE* cursor;
+ RNAClientAppModuleConfigItem* item;
+
+ for (item = (RNAClientAppModuleConfigItem*)sflist_first(config, &cursor);
+ item;
+ item = (RNAClientAppModuleConfigItem*)sflist_next(&cursor))
+ {
+ DebugFormat(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++)
+ {
+ DebugFormat(DEBUG_LOG,"registering patterns: %s: %d\n",
+ (const char*)patterns[i].pattern, patterns[i].index);
+ init_api->RegisterPattern(&rtp_validate, IpProtocol::UDP, patterns[i].pattern,
+ patterns[i].length, patterns[i].index, init_api->pAppidConfig);
+ }
+ }
+
+ unsigned j;
+ for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++)
+ {
+ DebugFormat(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 CLIENT_APP_RETCODE rtp_validate(const uint8_t* data, uint16_t size, const int dir,
+ AppIdData* flowp, Packet*, struct Detector*, const AppIdConfig*)
+{
+ ClientRTPData* fd;
+ ClientRTPMsg* hdr;
+
+ if (!size)
+ return CLIENT_APP_INPROCESS;
+
+ fd = (ClientRTPData*)rtp_client_mod.api->data_get(flowp, rtp_client_mod.flow_data_index);
+ if (!fd)
+ {
+ fd = (ClientRTPData*)snort_calloc(sizeof(ClientRTPData));
+ rtp_client_mod.api->data_add(flowp, fd, rtp_client_mod.flow_data_index, &snort_free);
+ fd->state = RTP_STATE_CONNECTION;
+ }
+
+ switch (fd->state)
+ {
+ case RTP_STATE_CONNECTION:
+ if (size < sizeof(ClientRTPMsg))
+ return CLIENT_APP_EINVALID;
+ hdr = (ClientRTPMsg*)data;
+ if (hdr->vers > 2 || hdr->payloadtype > 34)
+ return CLIENT_APP_EINVALID;
+ if (dir == APP_ID_FROM_INITIATOR)
+ {
+ fd->init_seq = ntohs(hdr->seq);
+ fd->init_timestamp = ntohl(hdr->timestamp);
+ fd->init_ssrc = ntohl(hdr->ssrc);
+ fd->init_count++;
+ }
+ else
+ {
+ fd->resp_seq = ntohs(hdr->seq);
+ fd->resp_timestamp = ntohl(hdr->timestamp);
+ fd->resp_ssrc = ntohl(hdr->ssrc);
+ fd->resp_count++;
+ }
+ fd->state = RTP_STATE_CONTINUE;
+ return CLIENT_APP_INPROCESS;
+
+ case RTP_STATE_CONTINUE:
+ if (size < sizeof(ClientRTPMsg))
+ return CLIENT_APP_EINVALID;
+ hdr = (ClientRTPMsg*)data;
+ if (hdr->vers > 2)
+ return CLIENT_APP_EINVALID;
+ if (hdr->payloadtype > 34)
+ return CLIENT_APP_EINVALID;
+ if (dir == APP_ID_FROM_INITIATOR)
+ {
+ if ((ntohs(hdr->seq) != ++fd->init_seq) ||
+ (ntohl(hdr->ssrc) != fd->init_ssrc) ||
+ (ntohl(hdr->timestamp) < fd->init_timestamp))
+ return CLIENT_APP_EINVALID;
+ fd->init_timestamp = ntohl(hdr->timestamp);
+ if (++fd->init_count < NUMBER_OF_PACKETS)
+ return CLIENT_APP_INPROCESS;
+ }
+ else
+ {
+ if ((ntohs(hdr->seq) != ++fd->resp_seq) ||
+ (ntohl(hdr->ssrc) != fd->resp_ssrc) ||
+ (ntohl(hdr->timestamp) < fd->resp_timestamp))
+ return CLIENT_APP_EINVALID;
+ fd->resp_timestamp = ntohl(hdr->timestamp);
+ if (++fd->resp_count < NUMBER_OF_PACKETS)
+ return CLIENT_APP_INPROCESS;
+ }
+ break;
+
+ default:
+ return CLIENT_APP_INPROCESS;
+ }
+
+ rtp_client_mod.api->add_app(flowp, APP_ID_RTP, APP_ID_RTP, nullptr);
+ setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED);
+ return CLIENT_APP_SUCCESS;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// client_app_smtp.cc author Sourcefire Inc.
+
+#include "client_app_smtp.h"
+
+#include "main/snort_debug.h"
+#include "protocols/packet.h"
+#include "utils/sflsq.h"
+#include "utils/util.h"
+
+#include "app_info_table.h"
+#include "appid_api.h"
+#include "application_ids.h"
+
+#define UNIT_TESTING 0
+
+#if UNIT_TESTING
+#include "fw_appid.h"
+#endif
+
+enum SMTPState
+{
+ SMTP_STATE_NONE,
+ SMTP_STATE_HELO,
+ SMTP_STATE_MAIL_FROM,
+ SMTP_STATE_RCPT_TO,
+ SMTP_STATE_DATA,
+ SMTP_STATE_MESSAGE,
+ SMTP_STATE_GET_PRODUCT_VERSION,
+ SMTP_STATE_SKIP_LINE,
+ SMTP_STATE_CONNECTION_ERROR,
+ SMTP_STATE_STARTTLS
+};
+
+#define MAX_VERSION_SIZE 64
+#define MAX_HEADER_LINE_SIZE 1024
+
+#if UNIT_TESTING
+char* stateName [] =
+{
+ "SMTP_STATE_NONE",
+ "SMTP_STATE_HELO",
+ "SMTP_STATE_MAIL_FROM",
+ "SMTP_STATE_RCPT_TO",
+ "SMTP_STATE_DATA",
+ "SMTP_STATE_MESSAGE",
+ "SMTP_STATE_GET_PRODUCT_VERSION",
+ "SMTP_STATE_SKIP_LINE",
+ "SMTP_STATE_CONNECTION_ERROR",
+ "SMTP_STATE_STARTTLS"
+};
+#endif
+
+/* flag values for ClientSMTPData */
+#define CLIENT_FLAG_STARTTLS_SENT 0x01
+#define CLIENT_FLAG_SMTPS 0x02
+
+#define MAX_VERSION_SIZE 64
+struct ClientSMTPData
+{
+ int flags;
+ SMTPState state;
+ SMTPState nextstate;
+ uint8_t version[MAX_VERSION_SIZE];
+ unsigned pos;
+ uint8_t* headerline;
+};
+
+struct SMTP_CLIENT_APP_CONFIG
+{
+ int enabled;
+};
+
+THREAD_LOCAL SMTP_CLIENT_APP_CONFIG smtp_config;
+
+static CLIENT_APP_RETCODE smtp_init(const IniClientAppAPI* const init_api, SF_LIST* config);
+static CLIENT_APP_RETCODE smtp_validate(const uint8_t* data, uint16_t size, const int dir,
+ AppIdData* flowp, Packet* pkt, struct Detector* userData, const AppIdConfig* pConfig);
+
+SO_PUBLIC RNAClientAppModule smtp_client_mod =
+{
+ "SMTP", // name
+ IpProtocol::TCP, // proto
+ &smtp_init, // init
+ nullptr, // clean
+ &smtp_validate, // validate
+ 1, // minimum_matches
+ nullptr, // api
+ nullptr, // userData
+ 0, // precedence
+ nullptr, // finalize,
+ 1, // provides_user
+ 0 // flow_data_index
+};
+
+struct Client_App_Pattern
+{
+ const u_int8_t* pattern;
+ unsigned length;
+ int index;
+ unsigned appId;
+};
+
+#define HELO "HELO "
+#define EHLO "EHLO "
+#define MAILFROM "MAIL FROM:"
+#define RCPTTO "RCPT TO:"
+#define DATA "DATA"
+#define RSET "RSET"
+#define AUTH "AUTH PLAIN"
+#define STARTTLS "STARTTLS"
+
+#define STARTTLS_COMMAND_SUCCESS "220 "
+
+#define MICROSOFT "Microsoft "
+#define OUTLOOK "Outlook"
+#define EXPRESS "Express "
+#define IMO "IMO, "
+
+#define XMAILER "X-Mailer: "
+#define USERAGENT "User-Agent: "
+
+static const uint8_t APP_SMTP_OUTLOOK[] = "Microsoft Outlook";
+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_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 AppRegistryEntry appIdRegistry[] =
+{
+ { 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_init(const IniClientAppAPI* const init_api, SF_LIST* config)
+{
+ unsigned i;
+
+ smtp_config.enabled = 1;
+
+ if (config)
+ {
+ SF_LNODE* cursor;
+ RNAClientAppModuleConfigItem* item;
+
+ for (item = (RNAClientAppModuleConfigItem*)sflist_first(config, &cursor);
+ item;
+ item = (RNAClientAppModuleConfigItem*)sflist_next(&cursor))
+ {
+ DebugFormat(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_validate, IpProtocol::TCP, patterns[i].pattern,
+ patterns[i].length, patterns[i].index, init_api->pAppidConfig);
+ }
+ }
+
+ unsigned j;
+ for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++)
+ {
+ DebugFormat(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[j].appId);
+ init_api->RegisterAppId(&smtp_validate, appIdRegistry[j].appId,
+ appIdRegistry[j].additionalInfo, init_api->pAppidConfig);
+ }
+
+ return CLIENT_APP_SUCCESS;
+}
+
+static int ExtractVersion(ClientSMTPData* const fd, const uint8_t* product,
+ const uint8_t* data, AppIdData* flowp, Packet*)
+{
+ const u_int8_t* p;
+ u_int8_t* v;
+ u_int8_t* v_end;
+ unsigned len;
+ unsigned sublen;
+ AppId appId = (fd->flags & CLIENT_FLAG_SMTPS) ? APP_ID_SMTPS : APP_ID_SMTP;
+
+ 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 (data-p >= (int)sizeof(OUTLOOK) && memcmp(p, OUTLOOK, sizeof(OUTLOOK)-1) == 0)
+ {
+ p += sizeof(OUTLOOK) - 1;
+ if (p >= data)
+ return 1;
+ if (*p == ',')
+ {
+ p++;
+ if (p >= data || *p != ' ')
+ return 1;
+ p++;
+ if (p >= data || isspace(*p))
+ return 1;
+ for (v=fd->version; v<v_end && p < data; v++,p++)
+ {
+ *v = *p;
+ }
+ *v = 0;
+ smtp_client_mod.api->add_app(flowp, appId, APP_ID_OUTLOOK, (char*)fd->version);
+ return 0;
+ }
+ else if (*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; v<v_end && p < data; v++,p++)
+ {
+ *v = *p;
+ }
+ *v = 0;
+ smtp_client_mod.api->add_app(flowp, appId, 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)
+ return 1;
+ for (v=fd->version; v<v_end && p < data; v++,p++)
+ {
+ *v = *p;
+ }
+ *v = 0;
+ smtp_client_mod.api->add_app(flowp, appId, 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; v<v_end && p < data; v++,p++)
+ {
+ *v = *p;
+ }
+ *v = 0;
+ smtp_client_mod.api->add_app(flowp, appId, 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; v<v_end && p < data; v++,p++)
+ {
+ *v = *p;
+ }
+ *v = 0;
+ smtp_client_mod.api->add_app(flowp, appId, 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; v<v_end && p < data-1; v++,p++)
+ {
+ *v = *p;
+ }
+ *v = 0;
+ smtp_client_mod.api->add_app(flowp, appId, 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; v<v_end && p < data; v++,p++)
+ {
+ *v = *p;
+ }
+ *v = 0;
+ smtp_client_mod.api->add_app(flowp, appId, 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; v<v_end && p < data; v++,p++)
+ {
+ *v = *p;
+ }
+ *v = 0;
+ smtp_client_mod.api->add_app(flowp, appId, 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; v<v_end && p < data; v++,p++)
+ {
+ *v = *p;
+ }
+ *v = 0;
+ smtp_client_mod.api->add_app(flowp, appId, 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; v<v_end && p < data; v++,p++)
+ {
+ *v = *p;
+ }
+ *v = 0;
+ smtp_client_mod.api->add_app(flowp, appId, 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; v<v_end && p < data; v++,p++)
+ {
+ *v = *p;
+ }
+ *v = 0;
+ smtp_client_mod.api->add_app(flowp, appId, appId /*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; v<v_end && p < data; v++,p++)
+ {
+ *v = *p;
+ }
+ *v = 0;
+ smtp_client_mod.api->add_app(flowp, appId, 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; v<v_end && p < data; v++,p++)
+ {
+ *v = *p;
+ }
+ *v = 0;
+ smtp_client_mod.api->add_app(flowp, appId, 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; v<v_end && p < data; p++)
+ {
+ if (*p == 0x0A || *p == 0x0D || !isprint(*p))
+ break;
+ *v = *p;
+ v++;
+ }
+ *v = 0;
+ smtp_client_mod.api->add_app(flowp, appId, APP_ID_THUNDERBIRD,
+ (char*)fd->version);
+ return 0;
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
+static void freeData(void* data)
+{
+ ClientSMTPData* fd = (ClientSMTPData*)data;
+ snort_free(fd->headerline);
+ snort_free(fd);
+}
+
+static CLIENT_APP_RETCODE smtp_validate(const uint8_t* data, uint16_t size, const int dir,
+ AppIdData* flowp, Packet* pkt, struct Detector*, const AppIdConfig*)
+{
+ ClientSMTPData* fd;
+ const uint8_t* end;
+ unsigned len;
+#if UNIT_TESTING
+ SMTPState currState = SMTP_STATE_NONE;
+
+#endif
+
+ fd = (ClientSMTPData*)smtp_client_mod.api->data_get(flowp, smtp_client_mod.flow_data_index);
+ if (!fd)
+ {
+ fd = (ClientSMTPData*)snort_calloc(sizeof(ClientSMTPData));
+ if (!fd)
+ return CLIENT_APP_ENOMEM;
+ if (smtp_client_mod.api->data_add(flowp, fd, smtp_client_mod.flow_data_index, &freeData))
+ {
+ snort_free(fd);
+ return CLIENT_APP_ENOMEM;
+ }
+ fd->state = SMTP_STATE_HELO;
+ }
+
+ if (dir != APP_ID_FROM_INITIATOR)
+ {
+ if ( (fd->flags & CLIENT_FLAG_STARTTLS_SENT) &&
+ !memcmp(data,STARTTLS_COMMAND_SUCCESS,sizeof(STARTTLS_COMMAND_SUCCESS)-1) )
+ {
+ fd->flags &= ~(CLIENT_FLAG_STARTTLS_SENT);
+ fd->flags |= CLIENT_FLAG_SMTPS;
+ clearAppIdFlag(flowp, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS); // we no longer need
+ // to examine the
+ // response.
+ if (!getAppIdFlag(flowp, APPID_SESSION_DECRYPTED))
+ {
+ /* 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(flowp, APP_ID_SMTPS, APP_ID_SMTPS, nullptr);
+ goto done;
+ }
+ }
+ return CLIENT_APP_INPROCESS;
+ }
+ if (getAppIdFlag(flowp, APPID_SESSION_ENCRYPTED))
+ {
+ if (!getAppIdFlag(flowp, APPID_SESSION_DECRYPTED))
+ return CLIENT_APP_INPROCESS;
+ }
+
+ for (end = data + size; data < end; data++)
+ {
+#if 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
+ switch (fd->state)
+ {
+ case SMTP_STATE_HELO:
+ len = end - data;
+ if (*data == HELO[fd->pos])
+ {
+ fd->pos++;
+ if (fd->pos == strlen(HELO))
+ {
+ fd->pos = 0;
+ fd->nextstate = SMTP_STATE_MAIL_FROM;
+ fd->state = SMTP_STATE_SKIP_LINE;
+ }
+ }
+ else if (*data == EHLO[fd->pos])
+ {
+ fd->pos++;
+ if (fd->pos == strlen(EHLO))
+ {
+ fd->pos = 0;
+ fd->nextstate = SMTP_STATE_MAIL_FROM;
+ fd->state = SMTP_STATE_SKIP_LINE;
+ }
+ }
+ else
+ goto done;
+ break;
+
+ case SMTP_STATE_MAIL_FROM:
+ if (*data == MAILFROM[fd->pos])
+ {
+ fd->pos++;
+ if (fd->pos == strlen(MAILFROM))
+ {
+ fd->pos = 0;
+ fd->nextstate = SMTP_STATE_RCPT_TO;
+ fd->state = SMTP_STATE_SKIP_LINE;
+ }
+ }
+ else if (*data == RSET[fd->pos])
+ {
+ fd->pos++;
+ if (fd->pos == strlen(RSET))
+ {
+ fd->pos = 0;
+ fd->nextstate = fd->state;
+ fd->state = SMTP_STATE_SKIP_LINE;
+ }
+ }
+ else if (*data == AUTH[fd->pos])
+ {
+ fd->pos++;
+ if (fd->pos == strlen(AUTH))
+ {
+ fd->pos = 0;
+ fd->nextstate = fd->state;
+ fd->state = SMTP_STATE_SKIP_LINE;
+ }
+ }
+ else if (*data == STARTTLS[fd->pos])
+ {
+ fd->pos++;
+ if (fd->pos == strlen(STARTTLS))
+ {
+ fd->flags |= CLIENT_FLAG_STARTTLS_SENT;
+ fd->pos = 0;
+ fd->nextstate = fd->state;
+ fd->state = SMTP_STATE_SKIP_LINE;
+ setAppIdFlag(flowp, APPID_SESSION_ENCRYPTED);
+ }
+ }
+ else
+ goto done;
+ break;
+ case SMTP_STATE_RCPT_TO:
+ if (*data == RCPTTO[fd->pos])
+ {
+ fd->pos++;
+ if (fd->pos == strlen(RCPTTO))
+ {
+ fd->pos = 0;
+ fd->nextstate = SMTP_STATE_DATA;
+ fd->state = SMTP_STATE_SKIP_LINE;
+ }
+ }
+ else
+ goto done;
+ break;
+
+ case SMTP_STATE_DATA:
+ if (*data == DATA[fd->pos])
+ {
+ fd->pos++;
+ if (fd->pos == strlen(DATA))
+ {
+ fd->pos = 0;
+ fd->nextstate = SMTP_STATE_MESSAGE;
+ fd->state = SMTP_STATE_SKIP_LINE;
+ }
+ }
+ else if (*data == RCPTTO[fd->pos])
+ {
+ fd->pos++;
+ if (fd->pos == strlen(RCPTTO))
+ {
+ fd->pos = 0;
+ fd->nextstate = fd->state;
+ fd->state = SMTP_STATE_SKIP_LINE;
+ }
+ }
+ break;
+ case SMTP_STATE_MESSAGE:
+ if (*data == '.')
+ {
+ len = end - data;
+ if (len == 0 ||
+ (len >= 1 && data[1] == 0x0A) ||
+ (len >= 2 && data[1] == 0x0D && data[2] == 0x0A))
+ {
+ AppId appId = (fd->flags & CLIENT_FLAG_SMTPS) ? APP_ID_SMTPS : APP_ID_SMTP;
+ smtp_client_mod.api->add_app(flowp, appId, appId, nullptr);
+ goto done;
+ }
+ }
+ else if (*data == XMAILER[fd->pos])
+ {
+ fd->pos++;
+ if (fd->pos == strlen(XMAILER))
+ {
+ fd->pos = 0;
+ fd->state = SMTP_STATE_GET_PRODUCT_VERSION;
+ }
+ }
+ else if (*data == USERAGENT[fd->pos])
+ {
+ fd->pos++;
+ if (fd->pos == strlen(USERAGENT))
+ {
+ fd->pos = 0;
+ fd->state = SMTP_STATE_GET_PRODUCT_VERSION;
+ }
+ }
+ else if (!isprint(*data) && *data != 0x09)
+ goto done;
+ else
+ {
+ fd->pos = 0;
+ fd->nextstate = fd->state;
+ fd->state = SMTP_STATE_SKIP_LINE;
+ }
+ break;
+
+ case SMTP_STATE_GET_PRODUCT_VERSION:
+ if (*data == 0x0D)
+ {
+ if (fd->headerline && fd->pos)
+ {
+ ExtractVersion(fd, fd->headerline, fd->headerline + fd->pos, flowp, pkt);
+ snort_free(fd->headerline);
+ fd->headerline = nullptr;
+ }
+ goto done;
+ }
+ else if (!isprint(*data))
+ {
+ snort_free(fd->headerline);
+ fd->headerline = nullptr;
+ goto done;
+ }
+ else
+ {
+ if (!fd->headerline)
+ {
+ if (!(fd->headerline = (uint8_t*)snort_calloc(MAX_HEADER_LINE_SIZE)))
+ goto done;
+ }
+
+ if (fd->pos < (MAX_HEADER_LINE_SIZE-1))
+ fd->headerline[fd->pos++] = *data;
+ }
+ break;
+
+ case SMTP_STATE_SKIP_LINE:
+ if (*data == 0x0A)
+ {
+ fd->pos = 0;
+ fd->state = fd->nextstate;
+ fd->nextstate = SMTP_STATE_NONE;
+ }
+ else if (!(*data == 0x0D || isprint(*data)))
+ goto done;
+ break;
+
+ default:
+ goto done;
+ }
+ }
+ return CLIENT_APP_INPROCESS;
+
+done:
+ setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED);
+ return CLIENT_APP_SUCCESS;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// client_app_smtp.h author Sourcefire Inc.
+
+#ifndef CLIENT_APP_SMTP_H
+#define CLIENT_APP_SMTP_H
+
+#include "client_app_api.h"
+
+extern RNAClientAppModule smtp_client_mod;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// client_app_ssh.cc author Sourcefire Inc.
+
+#include "main/snort_debug.h"
+#include "protocols/packet.h"
+#include "utils/sflsq.h"
+#include "utils/util.h"
+
+#include "app_info_table.h"
+#include "application_ids.h"
+#include "client_app_api.h"
+
+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
+
+enum SSHClientState
+{
+ 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
+};
+
+enum SSH2HeaderState
+{
+ 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
+};
+
+enum SSH1HeaderState
+{
+ SSH1_HEADER_BEGIN,
+ SSH1_HEADER_PLEN,
+ SSH1_HEADER_FIND_CODE,
+ SSH1_HEADER_CODE,
+ SSH1_SESSION_KEY
+};
+
+struct ClientSSHData
+{
+ 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;
+};
+
+#pragma pack(1)
+
+struct ClientSSHKeyString
+{
+ uint32_t len;
+ uint8_t data;
+};
+
+struct ClientSSHMsg
+{
+ uint32_t len;
+ uint8_t plen;
+ uint8_t code;
+};
+
+struct ClientSSH2KeyExchange
+{
+ ClientSSHMsg msg;
+ uint8_t cookie[16];
+};
+
+struct ClientSSH1KeyExchangeV1
+{
+ uint32_t len;
+ uint8_t code;
+};
+
+struct ClientSSHKeyExchangeFinal
+{
+ uint8_t kex_pkt;
+ uint32_t future;
+};
+
+#pragma pack()
+
+struct SSH_CLIENT_CONFIG
+{
+ int enabled;
+};
+
+THREAD_LOCAL SSH_CLIENT_CONFIG ssh_client_config;
+
+static CLIENT_APP_RETCODE ssh_client_init(const IniClientAppAPI* const init_api, SF_LIST* config);
+static CLIENT_APP_RETCODE ssh_client_validate(const uint8_t* data, uint16_t size, const int dir,
+ AppIdData* flowp, Packet* pkt, struct Detector* userData,
+ const AppIdConfig* pConfig);
+
+SO_PUBLIC RNAClientAppModule ssh_client_mod =
+{
+ "SSH", // name
+ IpProtocol::TCP, // proto
+ &ssh_client_init, // init
+ nullptr, // clean
+ &ssh_client_validate, // validate
+ 1, // minimum_matches
+ nullptr, // api
+ nullptr, // userData
+ 0, // precedence
+ nullptr, // finalize,
+ 1, // provides_user
+ 0 // flow_data_index
+};
+
+struct Client_App_Pattern
+{
+ const u_int8_t* pattern;
+ unsigned length;
+ int index;
+ unsigned appId;
+};
+
+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 AppRegistryEntry 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 IniClientAppAPI* const init_api, SF_LIST* config)
+{
+ unsigned i;
+
+ ssh_client_config.enabled = 1;
+
+ if (config)
+ {
+ SF_LNODE* cursor;
+ RNAClientAppModuleConfigItem* item;
+
+ for (item = (RNAClientAppModuleConfigItem*)sflist_first(config, &cursor);
+ item;
+ item = (RNAClientAppModuleConfigItem*)sflist_next(&cursor))
+ {
+ DebugFormat(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++)
+ {
+ DebugFormat(DEBUG_LOG, "registering patterns: %s: %d",
+ (const char*)patterns[i].pattern, patterns[i].index);
+ init_api->RegisterPattern(&ssh_client_validate, IpProtocol::TCP, patterns[i].pattern,
+ patterns[i].length, patterns[i].index, init_api->pAppidConfig);
+ }
+ }
+
+ unsigned j;
+ for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++)
+ {
+ DebugFormat(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;
+
+ default:
+ assert(0); // All cases should be handled above.
+ }
+ 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;
+ // Fall through to SSH1_HEADER_FIND_CODE state.
+ 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);
+ break;
+ case SSH1:
+ return ssh_client_validate_pubkey(offset, data, size, fd);
+ break;
+ default:
+ return CLIENT_APP_EINVALID;
+ break;
+ }
+ break;
+
+ 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,
+ AppIdData* flowp, Packet*, struct Detector*, const AppIdConfig*)
+{
+ ClientSSHData* fd;
+ CLIENT_APP_RETCODE sm_ret;
+
+ if (!size || dir != APP_ID_FROM_INITIATOR)
+ return CLIENT_APP_INPROCESS;
+
+ fd = ( ClientSSHData*)ssh_client_mod.api->data_get(flowp, ssh_client_mod.flow_data_index);
+ if (!fd)
+ {
+ fd = (ClientSSHData*)snort_calloc(sizeof(ClientSSHData));
+ ssh_client_mod.api->data_add(flowp, fd, ssh_client_mod.flow_data_index, &snort_free);
+ 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(flowp, APP_ID_SSH, fd->client_id, (const char*)fd->version);
+ setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED);
+ return CLIENT_APP_SUCCESS;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// client_app_timbuktu.cc author Sourcefire Inc.
+
+#include "main/snort_debug.h"
+#include "protocols/packet.h"
+#include "utils/sflsq.h"
+#include "utils/util.h"
+
+#include "app_info_table.h"
+#include "application_ids.h"
+#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
+
+enum TIMBUKTUState
+{
+ TIMBUKTU_STATE_BANNER = 0,
+ TIMBUKTU_STATE_ANY_MESSAGE_LEN,
+ TIMBUKTU_STATE_MESSAGE_LEN,
+ TIMBUKTU_STATE_MESSAGE_DATA
+};
+
+struct ClientTIMBUKTUData
+{
+ TIMBUKTUState state;
+ uint16_t stringlen;
+ unsigned pos;
+ union
+ {
+ uint16_t len;
+ uint8_t raw_len[2];
+ } l;
+};
+
+#pragma pack(1)
+struct ClientTIMBUKTUMsg
+{
+ uint16_t len;
+ uint8_t message;
+};
+#pragma pack()
+
+struct TIMBUKTU_CLIENT_APP_CONFIG
+{
+ int enabled;
+};
+
+THREAD_LOCAL TIMBUKTU_CLIENT_APP_CONFIG timbuktu_config;
+
+static CLIENT_APP_RETCODE timbuktu_init(const IniClientAppAPI* const init_api, SF_LIST* config);
+static CLIENT_APP_RETCODE timbuktu_validate(const uint8_t* data, uint16_t size, const int dir,
+ AppIdData* flowp, Packet* pkt, struct Detector* userData, const AppIdConfig* pConfig);
+
+SO_PUBLIC RNAClientAppModule timbuktu_client_mod =
+{
+ "TIMBUKTU", // name
+ IpProtocol::TCP, // proto
+ &timbuktu_init, // init
+ nullptr, // clean
+ &timbuktu_validate, // validate
+ 1, // minimum_matches
+ nullptr, // api
+ nullptr, // userData
+ 0, // precedence
+ nullptr, // finalize,
+ 1, // provides_user
+ 0 // flow_data_index
+};
+
+struct Client_App_Pattern
+{
+ const u_int8_t* pattern;
+ unsigned length;
+ int index;
+ unsigned appId;
+};
+
+static Client_App_Pattern patterns[] =
+{
+ { (const uint8_t*)TIMBUKTU_BANNER, sizeof(TIMBUKTU_BANNER)-1, 0, APP_ID_TIMBUKTU },
+};
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_TIMBUKTU, 0 }
+};
+
+static CLIENT_APP_RETCODE timbuktu_init(const IniClientAppAPI* const init_api, SF_LIST* config)
+{
+ unsigned i;
+
+ timbuktu_config.enabled = 1;
+
+ if (config)
+ {
+ SF_LNODE* cursor;
+ RNAClientAppModuleConfigItem* item;
+
+ for (item = (RNAClientAppModuleConfigItem*)sflist_first(config, &cursor);
+ item;
+ item = (RNAClientAppModuleConfigItem*)sflist_next(&cursor))
+ {
+ DebugFormat(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++)
+ {
+ DebugFormat(DEBUG_LOG,"registering patterns: %s: %d\n",
+ (const char*)patterns[i].pattern, patterns[i].index);
+ init_api->RegisterPattern(&timbuktu_validate, IpProtocol::TCP, patterns[i].pattern,
+ patterns[i].length, patterns[i].index, init_api->pAppidConfig);
+ }
+ }
+
+ unsigned j;
+ for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++)
+ {
+ DebugFormat(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,
+ AppIdData* flowp, Packet*, struct Detector*, const AppIdConfig*)
+{
+ ClientTIMBUKTUData* fd;
+ uint16_t offset;
+
+ if (dir != APP_ID_FROM_INITIATOR)
+ return CLIENT_APP_INPROCESS;
+
+ fd = (ClientTIMBUKTUData*)timbuktu_client_mod.api->data_get(flowp,
+ timbuktu_client_mod.flow_data_index);
+ if (!fd)
+ {
+ fd = (ClientTIMBUKTUData*)snort_calloc(sizeof(ClientTIMBUKTUData));
+ timbuktu_client_mod.api->data_add(flowp, fd,
+ timbuktu_client_mod.flow_data_index, &snort_free);
+ 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(flowp, APP_ID_TIMBUKTU, APP_ID_TIMBUKTU, nullptr);
+ setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED);
+ return CLIENT_APP_SUCCESS;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// client_app_tns.cc author Sourcefire Inc.
+
+#include "main/snort_debug.h"
+#include "protocols/packet.h"
+#include "utils/sflsq.h"
+#include "utils/util.h"
+
+#include "app_info_table.h"
+#include "application_ids.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)
+
+enum TNSState
+{
+ 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
+};
+
+#define MAX_VERSION_SIZE 12
+struct ClientTNSData
+{
+ 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;
+};
+
+#pragma pack(1)
+struct ClientTNSMsg
+{
+ uint16_t len;
+ uint16_t checksum;
+ uint8_t msg;
+ uint8_t res;
+ uint16_t hdchecksum;
+ uint8_t data;
+};
+#pragma pack()
+
+struct TNS_CLIENT_APP_CONFIG
+{
+ int enabled;
+};
+
+#ifdef REMOVED_WHILE_NOT_IN_USE
+static const char* msg_type[] =
+{
+ nullptr,
+ "Connect",
+ "Accept",
+ "Acknowledge",
+ "Refuse",
+ "Redirect",
+ "Data",
+ "Null",
+ nullptr,
+ "Abort",
+ nullptr,
+ "Resend",
+ "Marker",
+ "Attention",
+ "Control",
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr
+};
+#endif
+THREAD_LOCAL TNS_CLIENT_APP_CONFIG tns_config;
+
+static CLIENT_APP_RETCODE tns_init(const IniClientAppAPI* const init_api, SF_LIST* config);
+static CLIENT_APP_RETCODE tns_validate(const uint8_t* data, uint16_t size, const int dir,
+ AppIdData* flowp, Packet* pkt, struct Detector* userData, const AppIdConfig* pConfig);
+
+SO_PUBLIC RNAClientAppModule tns_client_mod =
+{
+ "TNS", // name
+ IpProtocol::TCP, // proto
+ &tns_init, // init
+ nullptr, // clean
+ &tns_validate, // validate
+ 1, // minimum_matches
+ nullptr, // api
+ nullptr, // userData
+ 0, // precedence
+ nullptr, // finalize,
+ 1, // provides_user
+ 0 // flow_data_index
+};
+
+struct Client_App_Pattern
+{
+ const u_int8_t* pattern;
+ unsigned length;
+ int index;
+ unsigned appId;
+};
+
+static Client_App_Pattern patterns[] =
+{
+ { (const uint8_t*)TNS_BANNER, sizeof(TNS_BANNER)-1, 2, APP_ID_ORACLE_DATABASE },
+};
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_ORACLE_DATABASE, APPINFO_FLAG_CLIENT_ADDITIONAL | APPINFO_FLAG_CLIENT_USER }
+};
+
+static CLIENT_APP_RETCODE tns_init(const IniClientAppAPI* const init_api, SF_LIST* config)
+{
+ unsigned i;
+
+ if (config)
+ {
+ SF_LNODE* cursor;
+ RNAClientAppModuleConfigItem* item;
+
+ for (item = (RNAClientAppModuleConfigItem*)sflist_first(config, &cursor);
+ item;
+ item = (RNAClientAppModuleConfigItem*)sflist_next(&cursor))
+ {
+ DebugFormat(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++)
+ {
+ DebugFormat(DEBUG_LOG,"registering patterns: %s: %d\n",
+ (const char*)patterns[i].pattern, patterns[i].index);
+ init_api->RegisterPattern(&tns_validate, IpProtocol::TCP, patterns[i].pattern,
+ patterns[i].length, patterns[i].index, init_api->pAppidConfig);
+ }
+ }
+
+ unsigned j;
+ for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++)
+ {
+ DebugFormat(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,
+ AppIdData* flowp, Packet*, struct Detector*, const AppIdConfig*)
+{
+ 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 = (ClientTNSData*)tns_client_mod.api->data_get(flowp, tns_client_mod.flow_data_index);
+ if (!fd)
+ {
+ fd = (ClientTNSData*)snort_calloc(sizeof(ClientTNSData));
+ tns_client_mod.api->data_add(flowp, fd, tns_client_mod.flow_data_index, &snort_free);
+ 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(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;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// client_app_vnc.cc author Sourcefire Inc.
+
+#include "main/snort_debug.h"
+#include "protocols/packet.h"
+#include "utils/sflsq.h"
+#include "utils/util.h"
+
+#include "app_info_table.h"
+#include "application_ids.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)
+
+enum VNCState
+{
+ VNC_STATE_BANNER = 0,
+ VNC_STATE_VERSION
+};
+
+#define MAX_VERSION_SIZE 8
+struct ClientVNCData
+{
+ VNCState state;
+ unsigned pos;
+ uint8_t version[MAX_VERSION_SIZE];
+};
+
+struct VNC_CLIENT_APP_CONFIG
+{
+ int enabled;
+};
+
+THREAD_LOCAL VNC_CLIENT_APP_CONFIG vnc_config;
+
+static CLIENT_APP_RETCODE vnc_init(const IniClientAppAPI* const init_api, SF_LIST* config);
+static CLIENT_APP_RETCODE vnc_validate(const uint8_t* data, uint16_t size, const int dir,
+ AppIdData* flowp, Packet* pkt, struct Detector* userData,
+ const AppIdConfig* pConfig);
+
+SO_PUBLIC RNAClientAppModule vnc_client_mod =
+{
+ "RFB", // name
+ IpProtocol::TCP, // proto
+ &vnc_init, // init
+ nullptr, // clean
+ &vnc_validate, // validate
+ 2, // minimum_matches
+ nullptr, // api
+ nullptr, // userData
+ 0, // precedence
+ nullptr, // finalize,
+ 1, // provides_user
+ 0 // flow_data_index
+};
+
+struct Client_App_Pattern
+{
+ const u_int8_t* pattern;
+ unsigned length;
+ int index;
+ unsigned appId;
+};
+
+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 AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_VNC, APPINFO_FLAG_CLIENT_ADDITIONAL },
+ { APP_ID_VNC_RFB, APPINFO_FLAG_CLIENT_ADDITIONAL }
+};
+
+static CLIENT_APP_RETCODE vnc_init(const IniClientAppAPI* const init_api, SF_LIST* config)
+{
+ unsigned i;
+
+ vnc_config.enabled = 1;
+
+ if (config)
+ {
+ SF_LNODE* cursor;
+ RNAClientAppModuleConfigItem* item;
+
+ for (item = (RNAClientAppModuleConfigItem*)sflist_first(config, &cursor);
+ item;
+ item = (RNAClientAppModuleConfigItem*)sflist_next(&cursor))
+ {
+ DebugFormat(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++)
+ {
+ DebugFormat(DEBUG_LOG,"registering patterns: %s: %d\n",
+ (const char*)patterns[i].pattern, patterns[i].index);
+ init_api->RegisterPattern(&vnc_validate, IpProtocol::TCP, patterns[i].pattern,
+ patterns[i].length, patterns[i].index, init_api->pAppidConfig);
+ }
+ }
+
+ unsigned j;
+ for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++)
+ {
+ DebugFormat(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,
+ AppIdData* flowp, Packet*, struct Detector*, const AppIdConfig*)
+{
+ ClientVNCData* fd;
+ uint16_t offset;
+
+ if (dir != APP_ID_FROM_INITIATOR)
+ return CLIENT_APP_INPROCESS;
+
+ fd = (ClientVNCData*)vnc_client_mod.api->data_get(flowp, vnc_client_mod.flow_data_index);
+ if (!fd)
+ {
+ fd = (ClientVNCData*)snort_calloc(sizeof(ClientVNCData));
+ vnc_client_mod.api->data_add(flowp, fd, vnc_client_mod.flow_data_index, &snort_free);
+ 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(flowp, APP_ID_VNC_RFB, APP_ID_VNC, (const char*)fd->version);
+ setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED);
+ return CLIENT_APP_SUCCESS;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// client_app_ym.cc author Sourcefire Inc.
+
+#include "client_app_ym.h"
+
+#include "main/snort_debug.h"
+#include "protocols/packet.h"
+#include "utils/sflsq.h"
+
+#include "app_info_table.h"
+#include "application_ids.h"
+#include "client_app_api.h"
+
+struct YM_CLIENT_APP_CONFIG
+{
+ int enabled;
+};
+
+THREAD_LOCAL YM_CLIENT_APP_CONFIG ym_config;
+
+#define MAX_VERSION_SIZE 64
+
+static CLIENT_APP_RETCODE ym_init(const IniClientAppAPI* const init_api, SF_LIST* config);
+static CLIENT_APP_RETCODE ym_validate(const uint8_t* data, uint16_t size, const int dir,
+ AppIdData* flowp, Packet* pkt, struct Detector* userData, const AppIdConfig* pConfig);
+
+RNAClientAppModule ym_client_mod =
+{
+ "YM", // name
+ IpProtocol::TCP, // proto
+ &ym_init, // init
+ nullptr, // clean
+ &ym_validate, // validate
+ 1, // minimum_matches
+ nullptr, // api
+ nullptr, // userData
+ 0, // precedence
+ nullptr, // finalize,
+ 1, // provides_user
+ 0 // flow_data_index
+};
+
+struct Client_App_Pattern
+{
+ const u_int8_t* pattern;
+ unsigned length;
+ int index;
+ unsigned appId;
+};
+
+static const uint8_t APP_YMSG[] = "YMSG";
+
+static Client_App_Pattern patterns[] =
+{
+ { APP_YMSG, sizeof(APP_YMSG)-1, 0, APP_ID_YAHOO_MSG },
+};
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_YAHOO, APPINFO_FLAG_CLIENT_ADDITIONAL },
+ { APP_ID_YAHOO_MSG, APPINFO_FLAG_CLIENT_ADDITIONAL }
+};
+
+static CLIENT_APP_RETCODE ym_init(const IniClientAppAPI* const init_api, SF_LIST* config)
+{
+ unsigned i;
+
+ ym_config.enabled = 1;
+
+ if (config)
+ {
+ SF_LNODE* cursor;
+ RNAClientAppModuleConfigItem* item;
+
+ for (item = (RNAClientAppModuleConfigItem*)sflist_first(config, &cursor);
+ item;
+ item = (RNAClientAppModuleConfigItem*)sflist_next(&cursor))
+ {
+ DebugFormat(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++)
+ {
+ DebugFormat(DEBUG_LOG,"registering patterns: %s: %d\n",
+ (const char*)patterns[i].pattern, patterns[i].index);
+ init_api->RegisterPattern(&ym_validate, IpProtocol::TCP, patterns[i].pattern,
+ patterns[i].length, patterns[i].index, init_api->pAppidConfig);
+ }
+ }
+
+ unsigned j;
+ for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++)
+ {
+ DebugFormat(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,
+ AppIdData* flowp, Packet* pkt, Detector*, const AppIdConfig*)
+{
+#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));
+
+ DebugFormat(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(flowp, APP_ID_YAHOO, product_id, (char*)version);
+ setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED);
+ return CLIENT_APP_SUCCESS;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// client_app_ym.h author Sourcefire Inc.
+
+#ifndef CLIENT_APP_YM_H
+#define CLIENT_APP_YM_H
+
+#include "client_app_api.h"
+
+extern RNAClientAppModule ym_client_mod;
+
+#endif
+
--- /dev/null
+set ( DETECTOR_PLUGINS_SOURCES
+ detector_api.h
+ detector_base.cc
+ detector_base.h
+ detector_dns.cc
+ detector_dns.h
+ detector_http.cc
+ detector_http.h
+ detector_imap.cc
+ detector_kerberos.cc
+ detector_pattern.cc
+ detector_pattern.h
+ detector_pop3.cc
+ detector_sip.cc
+ detector_sip.h
+ http_url_patterns.cc
+ http_url_patterns.h
+ )
+
+add_library ( appid_detector_plugins STATIC
+ ${DETECTOR_PLUGINS_SOURCES}
+)
+
+target_include_directories ( appid_detector_plugins PRIVATE ${APPID_INCLUDE_DIR} )
+
+# FIXIT-H: Add unit tests
+
+#install (FILES ${DETECTOR_PLUGINS_INCLUDES}
+# DESTINATION "${INCLUDE_INSTALL_PATH}/appid/detector_plugins"
+#)
--- /dev/null
+
+AM_CPPFLAGS+=-I$(top_srcdir)/src/network_inspectors/appid
+
+file_list = \
+detector_api.h \
+detector_base.cc \
+detector_base.h \
+detector_dns.cc \
+detector_dns.h \
+detector_http.cc \
+detector_http.h \
+detector_imap.cc \
+detector_kerberos.cc \
+detector_pattern.cc \
+detector_pattern.h \
+detector_pop3.cc \
+detector_sip.cc \
+detector_sip.h \
+http_url_patterns.cc \
+http_url_patterns.h
+
+noinst_LIBRARIES = libappid_detector_plugins.a
+libappid_detector_plugins_a_SOURCES = $(file_list)
+
+# Uncomment once tests are ready.
+#if BUILD_UNIT_TESTS
+#SUBDIRS = test
+#endif
+
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// detector_api.h author Sourcefire Inc.
+
+#ifndef DETECTOR_API_H
+#define DETECTOR_API_H
+
+#include "appid_flow_data.h"
+
+struct RNAServiceValidationModule;
+struct RNAClientAppModule;
+struct StreamAPI;
+
+using DetectorFlowdataGet = void*(*)(AppIdData*, unsigned);
+using DetectorFlowdataAdd = int(*)(AppIdData*, void*, unsigned, AppIdFreeFCN);
+
+struct DetectorApi
+{
+ DetectorFlowdataGet data_get;
+ DetectorFlowdataAdd data_add;
+};
+
+// compound detector with both service and client side.
+struct RNADetectorValidationModule
+{
+ /**service side.*/
+ RNAServiceValidationModule* service;
+
+ /**client side.*/
+ RNAClientAppModule* client;
+
+ const DetectorApi* api;
+ unsigned flow_data_index;
+ StreamAPI* streamAPI;
+};
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// detector_base.cc author Sourcefire Inc.
+
+#include "detector_base.h"
+
+#include "client_plugins/client_app_base.h"
+#include "detector_api.h"
+#include "log/messages.h"
+#include "service_plugins/service_base.h"
+
+static void* detector_flowdata_get(AppIdData* flowp, unsigned detector_id);
+static int detector_flowdata_add(AppIdData* flowp, void* data, unsigned detector_id,
+ AppIdFreeFCN fcn);
+
+static const DetectorApi detector_api
+{
+ &detector_flowdata_get,
+ &detector_flowdata_add,
+};
+
+extern RNADetectorValidationModule imap_detector_mod;
+extern RNADetectorValidationModule pop3_detector_mod;
+extern RNADetectorValidationModule kerberos_detector_mod;
+extern RNADetectorValidationModule pattern_detector_mod;
+
+static RNADetectorValidationModule* static_detector_list[]
+{
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ &imap_detector_mod,
+ &pop3_detector_mod,
+ &kerberos_detector_mod
+#endif
+ nullptr
+};
+
+/**callback function for initializing static and dynamic detectors.
+ */
+static int detectorLoadCallback(void* symbol)
+{
+ static THREAD_LOCAL unsigned detector_module_index = 0;
+ RNADetectorValidationModule* svm = (RNADetectorValidationModule*)symbol;
+
+ if (detector_module_index >= 65536)
+ {
+ ErrorMessage("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; FIXIT - removed to permit compilation.
+ detector_module_index++;
+
+ return 0;
+}
+
+int LoadDetectorModules(const char** )
+{
+ unsigned i;
+
+ for (i=0; i<sizeof(static_detector_list)/sizeof(*static_detector_list); i++)
+ {
+ if (static_detector_list[i] && detectorLoadCallback(static_detector_list[i]))
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+* A method to get client app specific state data from a flow
+*
+* @param flow the flow that contains the data
+*
+* @return RNA flow data structure for success
+*/
+static void* detector_flowdata_get(AppIdData* flowp, unsigned detector_id)
+{
+ return AppIdFlowdataGet(flowp, detector_id);
+}
+
+/**
+* A method to add client app specific state data to a flow
+*
+* @param flow the flow to which the data is added
+* @param data the data to add
+* @param id the data identifier
+*
+* @return RNA flow data structure for success
+*/
+static int detector_flowdata_add(AppIdData* flowp, void* data, unsigned detector_id,
+ AppIdFreeFCN fcn)
+{
+ return AppIdFlowdataAdd(flowp, data, detector_id, fcn);
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// detector_base.h author Sourcefire Inc.
+
+#ifndef DETECTOR_BASE_H
+#define DETECTOR_BASE_H
+
+int LoadDetectorModules(const char** dir_list);
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// detector_dns.cc author Sourcefire Inc.
+
+#include "detector_dns.h"
+
+#include "main/snort_debug.h"
+#include "utils/util.h"
+
+#include "appid_module.h"
+#include "app_info_table.h"
+#include "application_ids.h"
+#include "dns_defs.h"
+
+#include "client_plugins/client_app_api.h"
+#include "service_plugins/service_api.h"
+#include "service_plugins/service_config.h"
+
+enum DNSState
+{
+ DNS_STATE_QUERY,
+ DNS_STATE_RESPONSE
+};
+
+struct ServiceDNSData
+{
+ DNSState state;
+ uint16_t id;
+};
+
+struct MatchedDNSPatterns
+{
+ DNSHostPattern* mpattern;
+ int index;
+ MatchedDNSPatterns* next;
+};
+
+static int dns_service_init(const IniServiceAPI* const);
+static int dns_udp_validate(ServiceValidationArgs*);
+static int dns_tcp_validate(ServiceValidationArgs*);
+
+static RNAServiceElement udp_svc_element =
+{
+ nullptr, // next
+ &dns_udp_validate, // validate
+ nullptr, // userdata
+ DETECTOR_TYPE_DECODER, // detectorType
+ 1, // ref_count
+ 1, // current_ref_count
+ 0, // provides_user
+ "dns" // name
+};
+
+static RNAServiceElement tcp_svc_element =
+{
+ nullptr, // next
+ &dns_tcp_validate, // validate
+ nullptr, // userdata
+ DETECTOR_TYPE_DECODER, // detectorType
+ 1, // ref_count
+ 1, // current_ref_count
+ 0, // provides_user
+ "tcp dns" // name
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &dns_tcp_validate, 53, IpProtocol::TCP, 0 },
+ { &dns_udp_validate, 53, IpProtocol::UDP, 0 },
+ { &dns_udp_validate, 53, IpProtocol::UDP, 1 },
+ { &dns_tcp_validate, 5300, IpProtocol::TCP, 0 },
+ { &dns_udp_validate, 5300, IpProtocol::UDP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+RNAServiceValidationModule dns_service_mod =
+{
+ "DNS", // name
+ &dns_service_init, // init
+ pp, // pp
+ nullptr, // api
+ nullptr, // next
+ 0, // provides_user
+ nullptr, // clean
+ 0 // flow_data_index
+};
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_DNS, APPINFO_FLAG_SERVICE_UDP_REVERSED | APPINFO_FLAG_SERVICE_ADDITIONAL }
+};
+
+static CLIENT_APP_RETCODE dns_udp_client_init(const IniClientAppAPI* const, SF_LIST*);
+static CLIENT_APP_RETCODE dns_tcp_client_init(const IniClientAppAPI* const, SF_LIST*);
+static CLIENT_APP_RETCODE dns_udp_client_validate(
+ const uint8_t*, uint16_t, const int, AppIdData*, Packet*, Detector*, const AppIdConfig*);
+
+static CLIENT_APP_RETCODE dns_tcp_client_validate(
+ const uint8_t*, uint16_t, const int, AppIdData*, Packet*, Detector*, const AppIdConfig*);
+
+SO_PUBLIC RNAClientAppModule dns_udp_client_mod =
+{
+ "DNS", // name
+ IpProtocol::UDP, // proto
+ &dns_udp_client_init, // init
+ nullptr, // clean
+ &dns_udp_client_validate, // validate
+ 1, // minimum_matches
+ nullptr, // api
+ nullptr, // userData
+ 0, // precedence
+ nullptr, // finalize
+ 0, // provides_user
+ 0, // flow_data_index
+};
+
+SO_PUBLIC RNAClientAppModule dns_tcp_client_mod =
+{
+ "DNS", // name
+ IpProtocol::TCP, // proto
+ &dns_tcp_client_init, // init
+ nullptr, // clean
+ &dns_tcp_client_validate, // validate
+ 1, // minimum_matches
+ nullptr, // api
+ nullptr, // userData
+ 0, // precedence
+ nullptr, // finalize
+ 0, // provides_user
+ 0, // flow_data_index
+};
+
+static CLIENT_APP_RETCODE dns_udp_client_init(const IniClientAppAPI* const, SF_LIST*)
+{ return CLIENT_APP_SUCCESS; }
+
+static CLIENT_APP_RETCODE dns_tcp_client_init(const IniClientAppAPI* const, SF_LIST*)
+{ return CLIENT_APP_SUCCESS; }
+
+static CLIENT_APP_RETCODE dns_udp_client_validate(
+ const uint8_t*, uint16_t, const int, AppIdData*, Packet*, Detector*, const AppIdConfig*)
+{ return CLIENT_APP_INPROCESS; }
+
+static CLIENT_APP_RETCODE dns_tcp_client_validate(
+ const uint8_t*, uint16_t, const int, AppIdData*, Packet*, Detector*, const AppIdConfig*)
+{ return CLIENT_APP_INPROCESS; }
+
+static int dns_host_pattern_match(void* id, void*, int index, void* data, void*)
+{
+ MatchedDNSPatterns* cm;
+ MatchedDNSPatterns** matches = (MatchedDNSPatterns**)data;
+ DNSHostPattern* target = (DNSHostPattern*)id;
+
+ cm = (MatchedDNSPatterns*)snort_calloc(sizeof(MatchedDNSPatterns));
+ cm->mpattern = target;
+ cm->index = index;
+ cm->next = *matches;
+ *matches = cm;
+
+ return 0;
+}
+
+static int dns_host_detector_create_matcher(ServiceDnsConfig* pDnsConfig,
+ DetectorDNSHostPattern* list)
+{
+ DetectorDNSHostPattern* element = nullptr;
+
+ if (pDnsConfig->dns_host_host_matcher)
+ delete pDnsConfig->dns_host_host_matcher;
+
+ pDnsConfig->dns_host_host_matcher = new SearchTool("ac_full");
+ if (!pDnsConfig->dns_host_host_matcher)
+ return 0;
+
+ /* Add patterns from Lua API */
+ for (element = list; element; element = element->next)
+ {
+ pDnsConfig->dns_host_host_matcher->add((char*)element->dpattern->pattern,
+ element->dpattern->pattern_size, element->dpattern, true);
+ }
+
+ pDnsConfig->dns_host_host_matcher->prep();
+
+ return 1;
+}
+
+int dns_host_detector_process_patterns(ServiceDnsConfig* pDnsConfig)
+{
+ int retVal = 1;
+ if (!dns_host_detector_create_matcher(pDnsConfig, pDnsConfig->DetectorDNSHostPatternList))
+ retVal = 0;
+ return retVal;
+}
+
+static int dns_service_init(const IniServiceAPI* const init_api)
+{
+ unsigned i;
+ for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++)
+ {
+ DebugFormat(DEBUG_INSPECTOR, "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)
+ {
+ (*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;
+ }
+ }
+ return SERVICE_NOMATCH;
+}
+
+static int dns_validate_query(const uint8_t* data, uint16_t* offset, uint16_t size,
+ uint16_t id, unsigned host_reporting, AppIdData* 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;
+
+ 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 == 0) || (!host_len_valid))
+ {
+ host = nullptr;
+ 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:
+ dns_service_mod.api->add_dns_query_info(flowp, id, host, host_len, host_offset,
+ record_type);
+ break;
+ case PATTERN_PTR_REC:
+ dns_service_mod.api->add_dns_query_info(flowp, id, nullptr, 0, 0, record_type);
+ 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, AppIdData* 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:
+ dns_service_mod.api->add_dns_response_info(flowp, id, nullptr, 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 = nullptr;
+ 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, AppIdData* 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,
+ unsigned host_reporting, AppIdData* 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; i<count; i++)
+ {
+ if (dns_validate_query(data, &offset, size, ntohs(hdr->id), host_reporting, flowp) !=
+ SERVICE_SUCCESS)
+ {
+ return SERVICE_NOMATCH;
+ }
+ }
+ }
+
+ if (hdr->ANCount)
+ {
+ count = ntohs(hdr->ANCount);
+ for (i=0; i<count; i++)
+ {
+ if (dns_validate_answer(data, &offset, size, ntohs(hdr->id), hdr->RCODE,
+ host_reporting, flowp) != SERVICE_SUCCESS)
+ {
+ return SERVICE_NOMATCH;
+ }
+ }
+ }
+
+ if (hdr->NSCount)
+ {
+ count = ntohs(hdr->NSCount);
+ for (i=0; i<count; i++)
+ {
+ if (dns_validate_answer(data, &offset, size, ntohs(hdr->id), hdr->RCODE,
+ host_reporting, flowp) != SERVICE_SUCCESS)
+ {
+ return SERVICE_NOMATCH;
+ }
+ }
+ }
+
+ if (hdr->ARCount)
+ {
+ count = ntohs(hdr->ARCount);
+ for (i=0; i<count; i++)
+ {
+ if (dns_validate_answer(data, &offset, size, ntohs(hdr->id), 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), nullptr, 0, 0,
+ hdr->RCODE,
+ 0);
+
+ return SERVICE_SUCCESS;
+}
+
+static int dns_udp_validate(ServiceValidationArgs* args)
+{
+ int rval;
+ AppIdData* 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,
+ pAppidActiveConfig->mod_config->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,
+ pAppidActiveConfig->mod_config->dns_host_reporting, flowp);
+ if (rval == SERVICE_SUCCESS)
+ goto inprocess;
+ }
+ goto invalid;
+ }
+ else
+ {
+ // To get here, we missed the initial query, but now we've got
+ // a response.
+ rval = validate_packet(data, size, dir,
+ pAppidActiveConfig->mod_config->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,
+ pAppidActiveConfig->mod_config->dns_host_reporting, flowp);
+ if ((rval == SERVICE_SUCCESS) && (dir == APP_ID_FROM_INITIATOR))
+ goto inprocess;
+
+udp_done:
+ switch (rval)
+ {
+ case SERVICE_SUCCESS:
+success:
+ setAppIdFlag(flowp, APPID_SESSION_CONTINUE);
+ dns_service_mod.api->add_service(flowp, args->pkt, dir, &udp_svc_element,
+ APP_ID_DNS, nullptr, nullptr, nullptr);
+ appid_stats.dns_udp_count++;
+ 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);
+ 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);
+ return SERVICE_NOMATCH;
+ case SERVICE_INPROCESS:
+inprocess:
+ dns_udp_client_mod.api->add_app(flowp, APP_ID_NONE, APP_ID_DNS, nullptr);
+ dns_service_mod.api->service_inprocess(flowp, args->pkt, dir, &udp_svc_element);
+ return SERVICE_INPROCESS;
+ default:
+ return rval;
+ }
+}
+
+static int dns_tcp_validate(ServiceValidationArgs* args)
+{
+ ServiceDNSData* dd;
+ const DNSTCPHeader* hdr;
+ uint16_t tmp;
+ int rval;
+ AppIdData* 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,
+ pAppidActiveConfig->mod_config->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, pAppidActiveConfig->mod_config->dns_host_reporting,
+ flowp);
+ if (rval != SERVICE_SUCCESS)
+ goto tcp_done;
+
+ dd = static_cast<ServiceDNSData*>(dns_service_mod.api->data_get(flowp,
+ dns_service_mod.flow_data_index));
+ if (!dd)
+ {
+ dd = static_cast<ServiceDNSData*>(snort_calloc(sizeof(ServiceDNSData)));
+ if (dns_service_mod.api->data_add(flowp, dd, dns_service_mod.flow_data_index, &snort_free))
+ 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;
+ goto inprocess;
+ }
+ 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:
+ setAppIdFlag(flowp, APPID_SESSION_CONTINUE);
+ dns_service_mod.api->add_service(flowp, args->pkt, dir, &tcp_svc_element,
+ APP_ID_DNS, nullptr, nullptr, nullptr);
+ appid_stats.dns_tcp_count++;
+ 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);
+ 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);
+ return SERVICE_NOMATCH;
+
+inprocess:
+ dns_tcp_client_mod.api->add_app(flowp, APP_ID_NONE, APP_ID_DNS, nullptr);
+ dns_service_mod.api->service_inprocess(flowp, args->pkt, dir, &tcp_svc_element);
+ return SERVICE_INPROCESS;
+}
+
+static int dns_host_scan_patterns(SearchTool* matcher, const u_int8_t* pattern, size_t size,
+ AppId* ClientAppId, AppId* payloadId)
+{
+ MatchedDNSPatterns* mp = nullptr;
+ MatchedDNSPatterns* tmpMp;
+ DNSHostPattern* best_match;
+
+ if (!matcher)
+ return 0;
+
+ matcher->find_all((char*)pattern, size, dns_host_pattern_match, false, &mp);
+
+ if (!mp)
+ return 0;
+
+ best_match = mp->mpattern;
+ tmpMp = mp->next;
+ snort_free(mp);
+
+ while ((mp = tmpMp))
+ {
+ tmpMp = mp->next;
+ if (mp->mpattern->pattern_size > best_match->pattern_size)
+ {
+ best_match = mp->mpattern;
+ }
+ snort_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, AppId* ClientAppId,
+ AppId* payloadId, const ServiceDnsConfig* pDnsConfig)
+{
+ return dns_host_scan_patterns(pDnsConfig->dns_host_host_matcher, pattern, size, ClientAppId,
+ payloadId);
+}
+
+void service_dns_host_clean(ServiceDnsConfig* pDnsConfig)
+{
+ if (pDnsConfig->dns_host_host_matcher )
+ {
+ delete pDnsConfig->dns_host_host_matcher;
+ pDnsConfig->dns_host_host_matcher = nullptr;
+ }
+}
+
+static int dns_add_pattern(DetectorDNSHostPattern** list, uint8_t* pattern_str, size_t
+ pattern_size, uint8_t type, AppId app_id)
+{
+ DetectorDNSHostPattern* new_dns_host_pattern;
+
+ new_dns_host_pattern = static_cast<DetectorDNSHostPattern*>(snort_calloc(
+ sizeof(DetectorDNSHostPattern)));
+ new_dns_host_pattern->dpattern = static_cast<DNSHostPattern*>(snort_calloc(
+ sizeof(DNSHostPattern)));
+
+ 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, AppId app_id,
+ ServiceDnsConfig* 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)
+ snort_free(tmp_pattern->dpattern->pattern);
+ free (tmp_pattern->dpattern);
+ }
+ snort_free(tmp_pattern);
+ }
+}
+
+void dns_detector_free_patterns(ServiceDnsConfig* 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 = static_cast<char*>(snort_calloc(host_len + 1)); // plus '\0' at end
+ src = host;
+ dst = str;
+ while (*src != 0)
+ {
+ len = *src;
+ src++;
+ if ((dstLen + len) <= host_len)
+ memcpy(dst, src, len);
+ else
+ {
+ // Malformed DNS host, return
+ snort_free(str);
+ return nullptr;
+ }
+ src += len;
+ dst += len;
+ *dst = '.';
+ dstLen += len + 1;
+ dst++;
+ }
+ str[host_len] = '\0'; // nullptr term
+ return str;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// detector_dns.h author Sourcefire Inc.
+
+#ifndef DETECTOR_DNS_H
+#define DETECTOR_DNS_H
+
+#include "service_plugins/service_api.h"
+
+struct RNAServiceValidationModule;
+struct RNAClientAppModule;
+struct ServiceDnsConfig;
+
+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, AppId*, AppId*, const ServiceDnsConfig*);
+void service_dns_host_clean(ServiceDnsConfig* pConfig);
+int dns_host_detector_process_patterns(ServiceDnsConfig* pConfig);
+int dns_add_host_pattern(uint8_t*, size_t, uint8_t, AppId, ServiceDnsConfig*);
+void dns_detector_free_patterns(ServiceDnsConfig* pConfig);
+char* dns_parse_host(const uint8_t* host, uint8_t host_len);
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// detector_http.cc author Sourcefire Inc.
+
+#include "detector_http.h"
+
+#include "search_engines/search_tool.h"
+#include "main/snort_debug.h"
+#include "sfip/sf_ip.h"
+
+#include "service_plugins/service_api.h"
+#include "service_plugins/service_util.h"
+#include "util/sf_mlmp.h"
+#include "utils/util.h"
+
+#include "app_info_table.h"
+#include "application_ids.h"
+#include "client_plugins/client_app_base.h"
+#include "http_url_patterns.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)"
+
+struct MatchedPatterns
+{
+ DetectorHTTPPattern* mpattern;
+ int index;
+ MatchedPatterns* next;
+};
+
+static DetectorHTTPPattern content_type_patterns[] =
+{
+ { SINGLE, 0, APP_ID_QUICKTIME, 0,
+ sizeof(QUICKTIME_BANNER)-1, (uint8_t*)QUICKTIME_BANNER, APP_ID_QUICKTIME },
+ { SINGLE, 0, APP_ID_MPEG, 0,
+ sizeof(MPEG_BANNER)-1, (uint8_t*)MPEG_BANNER, APP_ID_MPEG },
+ { SINGLE, 0, APP_ID_MPEG, 0,
+ sizeof(MPA_BANNER)-1, (uint8_t*)MPA_BANNER, APP_ID_MPEG },
+ { SINGLE, 0, APP_ID_MPEG, 0,
+ sizeof(MP4A_BANNER)-1, (uint8_t*)MP4A_BANNER, APP_ID_MPEG },
+ { SINGLE, 0, APP_ID_MPEG, 0,
+ sizeof(ROBUST_MPA_BANNER)-1, (uint8_t*)ROBUST_MPA_BANNER, APP_ID_MPEG },
+ { SINGLE, 0, APP_ID_MPEG, 0,
+ sizeof(XSCPLS_BANNER)-1, (uint8_t*)XSCPLS_BANNER, APP_ID_MPEG },
+ { SINGLE, 0, APP_ID_SHOCKWAVE, 0,
+ sizeof(SHOCKWAVE_BANNER)-1, (uint8_t*)SHOCKWAVE_BANNER, APP_ID_SHOCKWAVE },
+ { SINGLE, 0, APP_ID_RSS, 0,
+ sizeof(RSS_BANNER)-1, (uint8_t*)RSS_BANNER, APP_ID_RSS },
+ { SINGLE, 0, APP_ID_ATOM, 0,
+ sizeof(ATOM_BANNER)-1, (uint8_t*)ATOM_BANNER, APP_ID_ATOM },
+ { SINGLE, 0, APP_ID_MP4, 0,
+ sizeof(MP4_BANNER)-1, (uint8_t*)MP4_BANNER, APP_ID_MP4 },
+ { SINGLE, 0, APP_ID_WMV, 0,
+ sizeof(WMV_BANNER)-1, (uint8_t*)WMV_BANNER, APP_ID_WMV },
+ { SINGLE, 0, APP_ID_WMA, 0,
+ sizeof(WMA_BANNER)-1, (uint8_t*)WMA_BANNER, APP_ID_WMA },
+ { SINGLE, 0, APP_ID_WAV, 0,
+ sizeof(WAV_BANNER)-1, (uint8_t*)WAV_BANNER, APP_ID_WAV },
+ { SINGLE, 0, APP_ID_WAV, 0,
+ sizeof(X_WAV_BANNER)-1, (uint8_t*)X_WAV_BANNER, APP_ID_WAV },
+ { SINGLE, 0, APP_ID_WAV, 0,
+ sizeof(VND_WAV_BANNER)-1, (uint8_t*)VND_WAV_BANNER, APP_ID_WAV },
+ { SINGLE, 0, APP_ID_FLASH_VIDEO, 0,
+ sizeof(FLV_BANNER)-1, (uint8_t*)FLV_BANNER, APP_ID_FLASH_VIDEO },
+ { SINGLE, 0, APP_ID_FLASH_VIDEO, 0,
+ sizeof(M4V_BANNER)-1, (uint8_t*)M4V_BANNER, APP_ID_FLASH_VIDEO },
+ { SINGLE, 0, APP_ID_FLASH_VIDEO, 0,
+ sizeof(GPP_BANNER)-1, (uint8_t*)GPP_BANNER, APP_ID_FLASH_VIDEO },
+ { SINGLE, 0, APP_ID_GENERIC, 0,
+ sizeof(VIDEO_BANNER)-1, (uint8_t*)VIDEO_BANNER, APP_ID_GENERIC },
+ { SINGLE, 0, APP_ID_GENERIC, 0,
+ sizeof(AUDIO_BANNER)-1, (uint8_t*)AUDIO_BANNER, APP_ID_GENERIC },
+};
+
+static DetectorHTTPPattern via_http_detector_patterns[] =
+{
+ { SINGLE, APP_ID_SQUID, 0, 0,
+ SQUID_PATTERN_SIZE, (uint8_t*)SQUID_PATTERN, APP_ID_SQUID },
+};
+
+static DetectorHTTPPattern host_payload_http_detector_patterns[] =
+{
+ { SINGLE, 0, 0, APP_ID_MYSPACE,
+ MYSPACE_PATTERN_SIZE, (uint8_t*)MYSPACE_PATTERN, APP_ID_MYSPACE },
+ { SINGLE, 0, 0, APP_ID_GMAIL,
+ GMAIL_PATTERN_SIZE, (uint8_t*)GMAIL_PATTERN, APP_ID_GMAIL,},
+ { SINGLE, 0, 0, APP_ID_GMAIL,
+ GMAIL_PATTERN2_SIZE, (uint8_t*)GMAIL_PATTERN2, APP_ID_GMAIL,},
+ { SINGLE, 0, 0, APP_ID_AOL_EMAIL,
+ AOL_PATTERN_SIZE, (uint8_t*)AOL_PATTERN, APP_ID_AOL_EMAIL,},
+ { SINGLE, 0, 0, APP_ID_MICROSOFT_UPDATE,
+ MSUP_PATTERN_SIZE, (uint8_t*)MSUP_PATTERN, APP_ID_MICROSOFT_UPDATE,},
+ { SINGLE, 0, 0, APP_ID_MICROSOFT_UPDATE,
+ MSUP_PATTERN2_SIZE, (uint8_t*)MSUP_PATTERN2, APP_ID_MICROSOFT_UPDATE,},
+ { SINGLE, 0, 0, APP_ID_YAHOOMAIL,
+ YAHOO_MAIL_PATTERN_SIZE, (uint8_t*)YAHOO_MAIL_PATTERN, APP_ID_YAHOOMAIL,},
+ { SINGLE, 0, 0, APP_ID_YAHOO_TOOLBAR,
+ YAHOO_TB_PATTERN_SIZE, (uint8_t*)YAHOO_TB_PATTERN, APP_ID_YAHOO_TOOLBAR,},
+ { SINGLE, 0, 0, APP_ID_ADOBE_UPDATE,
+ ADOBE_UP_PATTERN_SIZE, (uint8_t*)ADOBE_UP_PATTERN, APP_ID_ADOBE_UPDATE,},
+ { SINGLE, 0, 0, APP_ID_HOTMAIL,
+ HOTMAIL_PATTERN1_SIZE, (uint8_t*)HOTMAIL_PATTERN1, APP_ID_HOTMAIL,},
+ { SINGLE, 0, 0, APP_ID_HOTMAIL,
+ HOTMAIL_PATTERN2_SIZE, (uint8_t*)HOTMAIL_PATTERN2, APP_ID_HOTMAIL,},
+ { SINGLE, 0, 0, APP_ID_GOOGLE_TOOLBAR,
+ GOOGLE_TB_PATTERN_SIZE, (uint8_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, (uint8_t*)VERSION_PATTERN, FAKE_VERSION_APP_ID,},
+ { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_INTERNET_EXPLORER, 0,
+ sizeof(MSIE_PATTERN)-1, (uint8_t*)MSIE_PATTERN, APP_ID_INTERNET_EXPLORER,},
+ { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_KONQUEROR, 0,
+ sizeof(KONQUEROR_PATTERN)-1, (uint8_t*)KONQUEROR_PATTERN, APP_ID_KONQUEROR,},
+ { USER_AGENT_HEADER, APP_ID_SKYPE_AUTH, APP_ID_SKYPE, 0,
+ sizeof(SKYPE_PATTERN)-1, (uint8_t*)SKYPE_PATTERN, APP_ID_SKYPE,},
+ { USER_AGENT_HEADER, APP_ID_BITTORRENT, APP_ID_BITTORRENT, 0,
+ sizeof(BITTORRENT_PATTERN)-1, (uint8_t*)BITTORRENT_PATTERN, APP_ID_BITTORRENT,},
+ { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_FIREFOX, 0,
+ sizeof(FIREFOX_PATTERN)-1, (uint8_t*)FIREFOX_PATTERN, APP_ID_FIREFOX,},
+ { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_WGET, 0,
+ sizeof(WGET_PATTERN)-1, (uint8_t*)WGET_PATTERN, APP_ID_WGET,},
+ { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_CURL, 0,
+ sizeof(CURL_PATTERN)-1, (uint8_t*)CURL_PATTERN, APP_ID_CURL,},
+ { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_GOOGLE_DESKTOP, 0,
+ sizeof(GOOGLE_DESKTOP_PATTERN)-1, (uint8_t*)GOOGLE_DESKTOP_PATTERN, APP_ID_GOOGLE_DESKTOP,},
+ { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_PICASA, 0,
+ sizeof(PICASA_PATTERN)-1, (uint8_t*)PICASA_PATTERN, APP_ID_PICASA,},
+ { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_SAFARI, 0,
+ sizeof(SAFARI_PATTERN)-1, (uint8_t*)SAFARI_PATTERN, APP_ID_SAFARI,},
+ { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_CHROME, 0,
+ sizeof(CHROME_PATTERN)-1, (uint8_t*)CHROME_PATTERN, APP_ID_CHROME,},
+ { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_SAFARI_MOBILE_DUMMY, 0,
+ sizeof(MOBILE_PATTERN)-1, (uint8_t*)MOBILE_PATTERN, APP_ID_SAFARI_MOBILE_DUMMY,},
+ { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_BLACKBERRY_BROWSER, 0,
+ sizeof(BLACKBERRY_PATTERN)-1, (uint8_t*)BLACKBERRY_PATTERN, APP_ID_BLACKBERRY_BROWSER,},
+ { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_ANDROID_BROWSER, 0,
+ sizeof(ANDROID_PATTERN)-1, (uint8_t*)ANDROID_PATTERN, APP_ID_ANDROID_BROWSER,},
+ { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_WINDOWS_MEDIA_PLAYER, 0,
+ sizeof(MEDIAPLAYER_PATTERN)-1, (uint8_t*)MEDIAPLAYER_PATTERN, APP_ID_WINDOWS_MEDIA_PLAYER,},
+ { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_APPLE_EMAIL, 0,
+ sizeof(APPLE_EMAIL_PATTERN)-1, (uint8_t*)APPLE_EMAIL_PATTERN, APP_ID_APPLE_EMAIL,},
+};
+
+struct HeaderPattern
+{
+ int id;
+ uint8_t* data;
+ unsigned length;
+};
+
+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*, int index, void* data, void*)
+{
+ MatchedPatterns* cm;
+ MatchedPatterns** matches = (MatchedPatterns**)data;
+ DetectorHTTPPattern* target = (DetectorHTTPPattern*)id;
+
+ cm = (MatchedPatterns*)snort_calloc(sizeof(MatchedPatterns));
+ cm->mpattern = target;
+ cm->index = index;
+ cm->next = *matches;
+ *matches = cm;
+
+ return 0;
+}
+
+static int chp_pattern_match(void* id, void*, int index, void* data, void*)
+{
+ MatchedCHPAction* new_match;
+ MatchedCHPAction* current_search;
+ MatchedCHPAction* prev_search;
+ MatchedCHPAction** matches = (MatchedCHPAction**)data;
+ CHPAction* target = (CHPAction*)id;
+
+ new_match = (MatchedCHPAction*)snort_calloc(sizeof(MatchedCHPAction));
+ new_match->mpattern = target;
+ new_match->index = index;
+
+ // preserving order is required: sort by appIdInstance, then by precedence
+ for (current_search = *matches, prev_search = nullptr;
+ nullptr != current_search;
+ prev_search = current_search, current_search = current_search->next)
+ {
+ 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*)snort_calloc(sizeof(CHPMatchTally) +
+ ( CHP_TALLY_GROWTH_FACTOR * sizeof(CHPMatchCandidate)));
+ 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.
+}
+
+struct CHPTallyAndActions
+{
+ CHPMatchTally* pTally;
+ MatchedCHPAction* matches;
+};
+
+// 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*, int index, void* data, void*)
+{
+ 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, nullptr, index, &pTallyAndActions->matches, nullptr);
+}
+
+static int http_pattern_match(void* id, void*, int index, void* data, void*)
+{
+ MatchedPatterns* cm = nullptr;
+ 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)
+ {
+ cm = (MatchedPatterns*)snort_calloc(sizeof(MatchedPatterns));
+ cm->mpattern = target;
+ cm->index = index;
+ cm->next = nullptr;
+ *tmp = cm;
+ }
+
+ /* if its one of the host patterns, return after first match*/
+ if (cm->mpattern->seq == SINGLE)
+ return 1;
+ else
+ return 0;
+}
+
+static SearchTool* processPatterns(DetectorHTTPPattern* patternList,
+ size_t patternListCount, size_t*, HTTPListElement* luaPatternList)
+{
+ SearchTool* patternMatcher = new SearchTool("ac_full");
+
+ for (uint32_t i = 0; i < patternListCount; i++)
+ patternMatcher->add(patternList[i].pattern, patternList[i].pattern_size,
+ &patternList[i], false);
+
+ /* Add patterns from Lua API */
+ HTTPListElement* element;
+ for (element = luaPatternList; element != 0; element = element->next)
+ patternMatcher->add(element->detectorHTTPPattern.pattern,
+ element->detectorHTTPPattern.pattern_size, &element->detectorHTTPPattern, false);
+
+ patternMatcher->prep();
+ return patternMatcher;
+}
+
+static int processHostPatterns(
+ DetectorHTTPPattern* patternList,
+ size_t patternListCount,
+ HTTPListElement* luaPatternList,
+ DetectorAppUrlList* urlPatternList,
+ DetectorAppUrlList* RTMPUrlList,
+ DetectorHttpConfig* pHttpConfig
+ )
+{
+ HTTPListElement* element;
+ DetectorAppUrlPattern* appUrlPattern;
+
+ if (!pHttpConfig->hosUrlMatcher)
+ pHttpConfig->hosUrlMatcher = mlmpCreate();
+
+ if (!pHttpConfig->RTMPHosUrlMatcher)
+ pHttpConfig->RTMPHosUrlMatcher = mlmpCreate();
+
+ for (uint32_t i = 0; i < patternListCount; i++)
+ {
+ if (addMlmpPattern(pHttpConfig->hosUrlMatcher, &pHttpConfig->hosUrlPatternsList,
+ patternList[i].pattern, patternList[i].pattern_size,
+ nullptr, 0, nullptr, 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->hosUrlMatcher, &pHttpConfig->hosUrlPatternsList,
+ element->detectorHTTPPattern.pattern, element->detectorHTTPPattern.pattern_size,
+ nullptr, 0, nullptr, 0, element->detectorHTTPPattern.appId,
+ element->detectorHTTPPattern.payload, element->detectorHTTPPattern.service_id,
+ element->detectorHTTPPattern.client_app, element->detectorHTTPPattern.seq) < 0)
+ return -1;
+ }
+
+ for (uint32_t i = 0; i < RTMPUrlList->usedCount; i++)
+ {
+ appUrlPattern = RTMPUrlList->urlPattern[i];
+ if (addMlmpPattern(pHttpConfig->RTMPHosUrlMatcher, &pHttpConfig->hosUrlPatternsList,
+ 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 (uint32_t i = 0; i < urlPatternList->usedCount; i++)
+ {
+ appUrlPattern = urlPatternList->urlPattern[i];
+ if (addMlmpPattern(pHttpConfig->hosUrlMatcher, &pHttpConfig->hosUrlPatternsList,
+ 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->hosUrlMatcher);
+ mlmpProcessPatterns(pHttpConfig->RTMPHosUrlMatcher);
+ return 0;
+}
+
+static SearchTool* processContentTypePatterns(DetectorHTTPPattern* patternList,
+ size_t patternListCount, HTTPListElement* luaPatternList, size_t*)
+{
+ SearchTool* patternMatcher = new SearchTool("ac_full");
+ HTTPListElement* element;
+
+ for (uint32_t i = 0; i < patternListCount; i++)
+ {
+ patternMatcher->add(patternList[i].pattern,
+ patternList[i].pattern_size,
+ &patternList[i],
+ false);
+ }
+
+ /* Add patterns from Lua API */
+ for (element = luaPatternList; element; element = element->next)
+ {
+ patternMatcher->add(element->detectorHTTPPattern.pattern,
+ element->detectorHTTPPattern.pattern_size,
+ &element->detectorHTTPPattern,
+ false);
+ }
+
+ patternMatcher->prep();
+
+ return patternMatcher;
+}
+
+static int processCHPList(CHPListElement* chplist, DetectorHttpConfig* pHttpConfig)
+{
+ CHPListElement* chpe;
+
+ for (size_t i = 0; i < sizeof(pHttpConfig->chp_matchers)/sizeof(pHttpConfig->chp_matchers[0]);
+ i++)
+ {
+ pHttpConfig->chp_matchers[i] = new SearchTool("ac_full");
+ if (!pHttpConfig->chp_matchers[i])
+ return 0;
+ }
+
+ for (chpe = chplist; chpe; chpe = chpe->next)
+ {
+ pHttpConfig->chp_matchers[chpe->chp_action.ptype]->add(chpe->chp_action.pattern,
+ chpe->chp_action.psize,
+ &chpe->chp_action,
+ true);
+ }
+
+ for (size_t i = 0; i < sizeof(pHttpConfig->chp_matchers)/sizeof(pHttpConfig->chp_matchers[0]);
+ i++)
+ pHttpConfig->chp_matchers[i]->prep();
+
+ return 1;
+}
+
+static SearchTool* registerHeaderPatterns(
+ HeaderPattern* patternList,
+ size_t patternListCount)
+{
+ SearchTool* patternMatcher = new SearchTool("ac_full");
+
+ for (uint32_t i = 0; i < patternListCount; i++)
+ patternMatcher->add(patternList[i].data, patternList[i].length, &patternList[i], true);
+
+ patternMatcher->prep();
+
+ return patternMatcher;
+}
+
+int http_detector_finalize(AppIdConfig* pConfig)
+{
+ size_t upc = 0;
+ size_t apc = 0;
+ size_t ctc = 0;
+ size_t vpc = 0;
+
+ DetectorHttpConfig* pHttpConfig = &pConfig->detectorHttpConfig;
+ HttpPatternLists* patternLists = &pConfig->httpPatternLists;
+ uint32_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,
+ nullptr);
+ if (!pHttpConfig->via_matcher)
+ return -1;
+
+ /*create url pattern matcher */
+ pHttpConfig->url_matcher = processPatterns(nullptr, 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;
+
+ 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(DetectorHttpConfig* pHttpConfig)
+{
+ if (pHttpConfig->via_matcher)
+ {
+ delete(pHttpConfig->via_matcher);
+ pHttpConfig->via_matcher = nullptr;
+ }
+ if (pHttpConfig->url_matcher)
+ {
+ delete(pHttpConfig->url_matcher);
+ pHttpConfig->url_matcher = nullptr;
+ }
+ if (pHttpConfig->client_agent_matcher)
+ {
+ delete(pHttpConfig->client_agent_matcher);
+ pHttpConfig->client_agent_matcher = nullptr;
+ }
+ if (pHttpConfig->header_matcher)
+ {
+ delete(pHttpConfig->header_matcher);
+ pHttpConfig->header_matcher = nullptr;
+ }
+ if (pHttpConfig->content_type_matcher)
+ {
+ delete(pHttpConfig->content_type_matcher);
+ pHttpConfig->content_type_matcher = nullptr;
+ }
+ if (pHttpConfig->chp_user_agent_matcher)
+ {
+ delete(pHttpConfig->chp_user_agent_matcher);
+ pHttpConfig->chp_user_agent_matcher = nullptr;
+ }
+ if (pHttpConfig->chp_host_matcher)
+ {
+ delete(pHttpConfig->chp_host_matcher);
+ pHttpConfig->chp_host_matcher = nullptr;
+ }
+ if (pHttpConfig->chp_uri_matcher)
+ {
+ delete(pHttpConfig->chp_uri_matcher);
+ pHttpConfig->chp_uri_matcher = nullptr;
+ }
+ if (pHttpConfig->chp_cookie_matcher)
+ {
+ delete(pHttpConfig->chp_cookie_matcher);
+ pHttpConfig->chp_cookie_matcher = nullptr;
+ }
+ if (pHttpConfig->chp_content_type_matcher)
+ {
+ delete(pHttpConfig->chp_content_type_matcher);
+ pHttpConfig->chp_content_type_matcher = nullptr;
+ }
+ if (pHttpConfig->chp_location_matcher)
+ {
+ delete(pHttpConfig->chp_location_matcher);
+ pHttpConfig->chp_location_matcher = nullptr;
+ }
+ if (pHttpConfig->chp_body_matcher)
+ {
+ delete(pHttpConfig->chp_body_matcher);
+ pHttpConfig->chp_body_matcher = nullptr;
+ }
+ if (pHttpConfig->chp_referer_matcher)
+ {
+ delete(pHttpConfig->chp_referer_matcher);
+ pHttpConfig->chp_referer_matcher = nullptr;
+ }
+
+ destroyHosUrlMatcher(&pHttpConfig->hosUrlMatcher);
+ destroyHosUrlMatcher(&pHttpConfig->RTMPHosUrlMatcher);
+ destroyHosUrlPatternList(&pHttpConfig->hosUrlPatternsList);
+}
+
+static inline void FreeMatchStructures(MatchedPatterns* mp)
+{
+ MatchedPatterns* tmp;
+
+ while (mp)
+ {
+ tmp = mp;
+ mp = mp->next;
+ snort_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 || strcasestr((const char*)buf, 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*)snort_calloc(maxs + 1);
+ 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 new_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 */
+ //new_size = old_size - percent_count*2; // FIXIT-L new_size was never used
+ 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 = nullptr;
+ 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) || (end && tmp < end))
+ end = tmp;
+ }
+ }
+ if (!end)
+ {
+ if ((tmp = strchr(begin, 0x0d)))
+ {
+ end = tmp;
+ }
+ if ((tmp = strchr(begin, 0x0a)))
+ {
+ if ((!end) || (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 <<octet*8;
+ //maybe this is an extraneous '.' at the end
+ if (octet == 0)
+ {
+ done = 1;
+ break;
+ }
+ octet--;
+ digit_count = 1;
+ tmp = 0;
+ }
+ }
+ // this might be a character right after the IP address
+ else if (octet == 0 && tmp && tmp <= 255)
+ {
+ ret_addr += tmp;
+ done = 1;
+ break;
+ }
+ // bail out if we see something funny
+ else
+ return 0;
+ }
+ if (octet || 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 IpProtocol 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 IpProtocol::PROTO_NOT_SET;
+
+ temp_protocol *= 10;
+ temp_protocol += new_digit;
+ }
+
+ return (IpProtocol)temp_protocol;
+}
+
+static void fflowCreate(char* adata, fflow_info* fflow,
+ Packet* p, AppId target_appid)
+{
+ char* saddr_string = nullptr;
+ char* daddr_string = nullptr;
+ char* sport_string = nullptr;
+ char* dport_string = nullptr;
+ char* protocol_string = nullptr;
+ char* appid = nullptr;
+ const sfip_t* sip;
+ const sfip_t* dip;
+ int temp_port = 0;
+ char* brk;
+
+ /*
+ 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_r(adata, " ", &brk)))
+ return;
+ if (!(sport_string = strtok_r(nullptr, " ", &brk)))
+ return;
+ if (!(daddr_string = strtok_r(nullptr, " ", &brk)))
+ return;
+ if (!(dport_string = strtok_r(nullptr, " ", &brk)))
+ return;
+ if (!(protocol_string = strtok_r(nullptr, " ", &brk)))
+ return;
+ if (!(appid = strtok_r(nullptr, " ", &brk)))
+ return;
+
+ switch (*saddr_string)
+ {
+ case 'S':
+ sip = p->ptrs.ip_api.get_src();
+ fflow->sip = sip->ip32[0];
+ break;
+ case 'D':
+ sip = p->ptrs.ip_api.get_dst();
+ fflow->sip = sip->ip32[0];
+ 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, nullptr, 10)))
+ fflow->sport = p->ptrs.sp + temp_port;
+ else
+ return;
+ }
+ else
+ fflow->sport = p->ptrs.sp;
+ break;
+ case 'D':
+ if (strlen(sport_string) > 2)
+ {
+ if ((temp_port = strtol(sport_string+1, nullptr, 10)))
+ fflow->sport = p->ptrs.dp + temp_port;
+ else
+ return;
+ }
+ else
+ fflow->sport = p->ptrs.dp;
+ 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 = p->ptrs.ip_api.get_src();
+ fflow->dip = dip->ip32[0];
+ break;
+ case 'D':
+ dip = p->ptrs.ip_api.get_dst();
+ fflow->dip = dip->ip32[0];
+ 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, nullptr, 10)))
+ fflow->dport = p->ptrs.dp + temp_port;
+ else
+ return;
+ }
+ else
+ fflow->dport = p->ptrs.sp;
+ break;
+ case 'D':
+ if (strlen(dport_string) > 2)
+ {
+ if ((temp_port = strtol(dport_string+1, nullptr, 10)))
+ fflow->dport = p->ptrs.dp + temp_port;
+ else
+ return;
+ }
+ else
+ fflow->dport = p->ptrs.dp;
+ 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 = IpProtocol::TCP;
+ break;
+ case 'U':
+ fflow->protocol = IpProtocol::UDP;
+ break;
+ case '0':
+ fflow->protocol = IpProtocol::PROTO_NOT_SET;
+ break;
+ case 'S':
+ case 'D':
+ fflow->protocol = p->is_tcp() ? IpProtocol::TCP : IpProtocol::UDP;
+ break;
+ case '*':
+ if ( fflow->protocol == IpProtocol::PROTO_NOT_SET )
+ return;
+ break;
+ default:
+ if ( fflow->protocol == IpProtocol::PROTO_NOT_SET )
+ {
+ fflow->protocol = ffSetProtocol(
+ protocol_string, strlen(protocol_string), 0, 0);
+
+ if ( fflow->protocol == IpProtocol::PROTO_NOT_SET )
+ return;
+ }
+ break;
+ }
+
+ switch (*appid)
+ {
+ case '*':
+ fflow->appId = target_appid;
+ break;
+ default:
+ fflow->appId = strtol(appid, nullptr, 10);
+ }
+
+ fflow->flow_prepared = 1;
+}
+
+void finalizeFflow(fflow_info* fflow, unsigned app_type_flags, AppId target_appId, Packet* p)
+{
+ AppIdData* fp;
+ sfip_t saddr, daddr;
+
+ sfip_set_raw(&saddr, &fflow->sip, AF_INET);
+ sfip_set_raw(&daddr, &fflow->dip, AF_INET);
+
+ if (!(fp = AppIdEarlySessionCreate(nullptr, 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 DetectorHttpConfig* pHttpConfig)
+{
+ CHPTallyAndActions tallyAndActions;
+ tallyAndActions.pTally = *ppTally;
+ tallyAndActions.matches = *ppmatches;
+
+ //FIXIT-H
+ pHttpConfig->chp_matchers[ptype]->find_all(buf, buf_size, &chp_key_pattern_match,
+ false, (void*)(&tallyAndActions));
+
+ *ppTally = tallyAndActions.pTally;
+ *ppmatches = tallyAndActions.matches;
+ return (int)(tallyAndActions.pTally != nullptr);
+}
+
+AppId scanCHP(PatternType ptype, char* buf, int buf_size, MatchedCHPAction* mp,
+ char** version, char** user, char** new_field,
+ int* total_found, httpSession* hsession, Packet* p, const
+ DetectorHttpConfig* pHttpConfig)
+{
+ MatchedCHPAction* second_sweep_for_inserts = nullptr;
+ int do_not_further_modify_field = 0;
+ CHPAction* match = nullptr;
+ AppId ret = APP_ID_NONE;
+ MatchedCHPAction* tmp;
+
+ if (ptype > MAX_KEY_PATTERN)
+ {
+ // There is no previous attempt to match generated by scanKeyCHP()
+ mp = nullptr;
+
+ // FIXIT-H
+ pHttpConfig->chp_matchers[ptype]->find_all(buf, buf_size, &chp_pattern_match,
+ false, (void*)(&mp));
+ }
+ if (!mp)
+ return APP_ID_NONE;
+
+ if (pAppidActiveConfig->mod_config->disable_safe_search)
+ {
+ new_field = nullptr;
+ }
+
+ 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)
+ {
+ 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 && !pAppidActiveConfig->mod_config->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 &&
+ nullptr != new_field &&
+ nullptr == *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 (pAppidActiveConfig->mod_config->chp_fflow_disabled)
+ break;
+ if (!hsession->fflow)
+ hsession->fflow = (fflow_info*)snort_calloc(sizeof(fflow_info));
+ if (!hsession->fflow->sip)
+ hsession->fflow->sip = ffSetIp(buf, buf_size, tmp->index, match->psize);
+ break;
+
+ case FUTURE_APPID_SESSION_DIP:
+ if (pAppidActiveConfig->mod_config->chp_fflow_disabled)
+ break;
+ if (!hsession->fflow)
+ hsession->fflow = (fflow_info*)snort_calloc(sizeof(fflow_info));
+ if (!hsession->fflow->dip)
+ hsession->fflow->dip = ffSetIp(buf, buf_size, tmp->index, match->psize);
+ break;
+
+ case FUTURE_APPID_SESSION_SPORT:
+ if (pAppidActiveConfig->mod_config->chp_fflow_disabled)
+ break;
+ if (!hsession->fflow)
+ hsession->fflow = (fflow_info*)snort_calloc(sizeof(fflow_info));
+ if (!hsession->fflow->sport)
+ hsession->fflow->sport = ffSetPort(buf, buf_size, tmp->index, match->psize);
+ break;
+
+ case FUTURE_APPID_SESSION_DPORT:
+ if (pAppidActiveConfig->mod_config->chp_fflow_disabled)
+ break;
+ if (!hsession->fflow)
+ hsession->fflow = (fflow_info*)snort_calloc(sizeof(fflow_info));
+ if (!hsession->fflow->dport)
+ hsession->fflow->dport = ffSetPort(buf, buf_size, tmp->index, match->psize);
+ break;
+
+ case FUTURE_APPID_SESSION_PROTOCOL:
+ if (pAppidActiveConfig->mod_config->chp_fflow_disabled)
+ break;
+ if (!hsession->fflow)
+ hsession->fflow = (fflow_info*)snort_calloc(sizeof(fflow_info));
+ if (hsession->fflow->protocol == IpProtocol::PROTO_NOT_SET)
+ hsession->fflow->protocol = ffSetProtocol(buf, buf_size, tmp->index, match->psize);
+ break;
+
+ case FUTURE_APPID_SESSION_CREATE:
+ if (pAppidActiveConfig->mod_config->chp_fflow_disabled)
+ break;
+ if (!hsession->fflow)
+ hsession->fflow = (fflow_info*)snort_calloc(sizeof(fflow_info));
+ fflowCreate(match->action_data, hsession->fflow, p, hsession->chp_candidate);
+ break;
+
+ case INSERT_FIELD:
+ if (!do_not_further_modify_field && second_sweep_for_inserts == nullptr)
+ {
+ 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, nullptr, 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;
+ }
+ }
+ // non-nullptr second_sweep_for_inserts indicates the insert action we will use.
+ if (!do_not_further_modify_field && second_sweep_for_inserts &&
+ nullptr != new_field &&
+ nullptr == *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)
+ snort_free(*optionalStr);
+
+ *optionalStr = snort_strdup(strToDup);
+ }
+ return 0;
+}
+
+void identifyUserAgent(const uint8_t* start, int size, AppId* serviceAppId, AppId* ClientAppId,
+ char** version,
+ const DetectorHttpConfig* 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 uint8_t* end;
+ MatchedPatterns* mp = nullptr;
+ MatchedPatterns* tmp;
+ DetectorHTTPPattern* match;
+ uint8_t* buffPtr;
+ unsigned int i;
+ char temp_ver[MAX_VERSION_SIZE];
+ temp_ver[0] = 0;
+
+ // FIXIT-H
+ pHttpConfig->client_agent_matcher->find_all((const char*)start, size, &http_pattern_match,
+ false, (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 = (uint8_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 = (uint8_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 = (uint8_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 = (uint8_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 = (uint8_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 = (uint8_t*)strstr((char*)start, (char*)APPLE_EMAIL_PATTERNS[i]);
+ appleEmailDetect = ((uint8_t*)buffPtr && (i != 0 || (i == 0 && buffPtr ==
+ ((uint8_t*)start))));
+ }
+ if (appleEmailDetect)
+ {
+ dominant_pattern_detected = !(buffPtr && strstr((char*)buffPtr,
+ SAFARI_PATTERN) != nullptr);
+ temp_ver[0] = 0;
+ *serviceAppId = APP_ID_HTTP;
+ *ClientAppId = match->client_app;
+ }
+ i = 0;
+ break;
+
+ case APP_ID_WGET:
+ buffPtr = (uint8_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 = (uint8_t*)start + tmp->index + match->pattern_size;
+ if (*buffPtr == (uint8_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 = (uint8_t*)start + tmp->index + match->pattern_size;
+ /* we may have to enter the pattern with the / in it. */
+ if (*buffPtr == (uint8_t)'/' || *buffPtr == (uint8_t)' ')
+ buffPtr++;
+ if (buffPtr-1 > start && buffPtr < end && (*(buffPtr-1) == (uint8_t)'/' ||
+ *(buffPtr-1) == (uint8_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 geAppidByViaPattern(const uint8_t* data, unsigned size, char** version, const
+ DetectorHttpConfig* pHttpConfig)
+{
+ unsigned i;
+ const uint8_t* data_ptr;
+ const uint8_t* end = data + size;
+ MatchedPatterns* mp = nullptr;
+ DetectorHTTPPattern* match = nullptr;
+ char temp_ver[MAX_VERSION_SIZE];
+
+ if (pHttpConfig->via_matcher)
+ {
+ // FIXIT-H
+ pHttpConfig->via_matcher->find_all((const char*)data, size, &http_pattern_match,
+ false, (void*)&mp);
+ }
+
+ if (mp)
+ {
+ match = (DetectorHTTPPattern*)mp->mpattern;
+ switch (match->service_id)
+ {
+ case APP_ID_SQUID:
+ data_ptr = (uint8_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/"
+
+AppId 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;
+}
+
+AppId geAppidByContentType(const uint8_t* data, int size, const DetectorHttpConfig* pHttpConfig)
+{
+ MatchedPatterns* mp = nullptr;
+ DetectorHTTPPattern* match;
+ AppId payloadId;
+
+ if (pHttpConfig->content_type_matcher)
+ {
+ // FIXIT-H
+ pHttpConfig->content_type_matcher->find_all((const char*)data, size,
+ &content_pattern_match, false, (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*, int index, void* data, void*)
+{
+ 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 DetectorHttpConfig* 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)
+ {
+ //FIXIT-H
+ pHttpConfig->header_matcher->find_all((const char*)data, size,
+ &http_header_pattern_match, false, (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;
+}
+
+AppId getAppIdFromUrl(char* host, char* url, char** version,
+ char* referer, AppId* ClientAppId, AppId* serviceAppId,
+ AppId* payloadAppId, AppId* referredPayloadAppId, unsigned from_rtmp,
+ const DetectorHttpConfig* pHttpConfig)
+{
+ char* path;
+ char* referer_start;
+ char* temp_host = nullptr;
+ const char* referer_path = nullptr;
+ int host_len;
+ int referer_len = 0;
+ int referer_path_len = 0;
+ int path_len;
+ tMlmpPattern patterns[3];
+ tMlpPattern query;
+ HosUrlDetectorPattern* data;
+ char* q;
+ int payload_found = 0;
+ int url_len;
+ static tMlmpTree* matcher;
+
+#define RTMP_MEDIA_STREAM_OFFSET 50000000
+#define URL_SCHEME_END_PATTERN "://"
+#define URL_SCHEME_MAX_LEN (sizeof("https://")-1)
+
+ matcher = (from_rtmp ? pHttpConfig->RTMPHosUrlMatcher : pHttpConfig->hosUrlMatcher);
+
+ if (!host && !url)
+ return 0;
+
+ if (url)
+ {
+ size_t 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 = snort_strdup(url);
+ host = strchr(host, '/');
+ if (host != nullptr)
+ {
+ *host = '\0';
+ }
+ host = temp_host;
+ }
+ host_len = strlen(host);
+
+ if (url_len)
+ {
+ if (url_len < host_len)
+ {
+ snort_free(temp_host);
+ return 0;
+ }
+ path_len = url_len - host_len;
+ path = url + host_len;
+ }
+ else
+ {
+ path = nullptr;
+ path_len = 0;
+ }
+
+ 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 = nullptr;
+
+ data = (HosUrlDetectorPattern*)mlmpMatchPatternUrl(matcher, patterns);
+
+ if (data)
+ {
+ payload_found = 1;
+ if (url)
+ {
+ q = strchr(url, '?');
+ if (q != nullptr)
+ {
+ 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;
+ }
+
+ snort_free(temp_host);
+
+ /* if referred_id feature id disabled, referer will be null */
+ if (referer && (!payload_found || appInfoEntryFlagGet(data->payload_id, APPINFO_FLAG_REFERRED,
+ pAppidActiveConfig)))
+ {
+ 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 = nullptr;
+ 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 = nullptr;
+ data = (HosUrlDetectorPattern*)mlmpMatchPatternUrl(matcher, patterns);
+ if (data != nullptr)
+ {
+ 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 = (const uint8_t*)memchr(data, '/', len);
+ if (ver)
+ {
+ version_len = 0;
+ vendor_len = ver - data;
+ ver++;
+ subname = nullptr;
+ subname_len = 0;
+ subver = nullptr;
+ paren = nullptr;
+ for (p=ver; *p && p < end; p++)
+ {
+ if (*p == '(')
+ {
+ subname = nullptr;
+ paren = p;
+ }
+ else if (*p == ')')
+ {
+ subname = nullptr;
+ paren = nullptr;
+ }
+ /* 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 = (RNAServiceSubtype*)snort_calloc(sizeof(RNAServiceSubtype));
+ tmp = (char*)snort_calloc(subname_len + 1);
+ memcpy(tmp, subname, subname_len);
+ tmp[subname_len] = 0;
+ sub->service = tmp;
+ subver_len = p - subver;
+ if (subver_len > 0 && *subver)
+ {
+ tmp = (char*)snort_calloc(subver_len + 1);
+ memcpy(tmp, subver, subver_len);
+ tmp[subver_len] = 0;
+ sub->version = tmp;
+ }
+ sub->next = *subtype;
+ *subtype = sub;
+ }
+ subname = p + 1;
+ subname_len = 0;
+ subver = nullptr;
+ }
+ 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 = (RNAServiceSubtype*)snort_calloc(sizeof(RNAServiceSubtype));
+ tmp = (char*)snort_calloc(subname_len + 1);
+ memcpy(tmp, subname, subname_len);
+ tmp[subname_len] = 0;
+ sub->service = tmp;
+
+ subver_len = p - subver;
+ if (subver_len > 0 && *subver)
+ {
+ tmp = (char*)snort_calloc(subver_len + 1);
+ memcpy(tmp, subver, subver_len);
+ tmp[subver_len] = 0;
+ sub->version = tmp;
+ }
+ 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;
+ *version = (char*)snort_calloc(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;
+ *vendor = (char*)snort_calloc(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 IniClientAppAPI* const init_api,
+ SF_LIST* config);
+static CLIENT_APP_RETCODE http_client_validate(const uint8_t* data, uint16_t size, const int dir,
+ AppIdData* flowp, Packet* pkt, struct Detector* userData,
+ const AppIdConfig* pConfig);
+
+static int http_service_init(const IniServiceAPI* const init_api);
+static int http_service_validate(ServiceValidationArgs* args);
+
+static AppRegistryEntry 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)
+
+struct Client_App_Pattern
+{
+ const uint8_t* pattern;
+ unsigned length;
+ int index;
+ unsigned appId;
+};
+
+static Client_App_Pattern patterns[] =
+{
+ {
+ (const uint8_t*)HTTP2_PREFACE,
+ sizeof(HTTP2_PREFACE)-1,
+ 0,
+ APP_ID_HTTP
+ },
+};
+
+SO_PUBLIC RNAClientAppModule http_client_mod =
+{
+ "HTTP",
+ IpProtocol::TCP,
+ &http_client_init,
+ nullptr,
+ &http_client_validate,
+ 1,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ 0
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ {
+ nullptr,
+ 0,
+ IpProtocol::PROTO_NOT_SET,
+ 0
+ }
+};
+
+static RNAServiceElement http_service_element =
+{
+ nullptr,
+ &http_service_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "http"
+};
+
+RNAServiceValidationModule http_service_mod =
+{
+ "HTTP",
+ &http_service_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static CLIENT_APP_RETCODE http_client_init(const IniClientAppAPI* const init_api, SF_LIST*)
+{
+ if (pAppidActiveConfig->mod_config->http2_detection_enabled)
+ {
+ for (unsigned i = 0; i < sizeof(patterns)/sizeof(*patterns); i++)
+ {
+ DebugFormat(DEBUG_LOG, "registering patterns: %s: %d",
+ (const char*)patterns[i].pattern, patterns[i].index);
+ init_api->RegisterPattern(&http_client_validate, IpProtocol::TCP, patterns[i].pattern,
+ patterns[i].length, patterns[i].index, init_api->pAppidConfig);
+ }
+ }
+
+ for (unsigned j = 0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++)
+ {
+ DebugFormat(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*, uint16_t, const int dir,
+ AppIdData* flowp, Packet* pkt, struct Detector*, const AppIdConfig*)
+{
+ http_client_mod.api->add_app(flowp, APP_ID_HTTP, APP_ID_HTTP + GENERIC_APP_OFFSET, nullptr);
+ flowp->rnaClientState = RNA_STATE_FINISHED;
+ http_service_mod.api->add_service(flowp, pkt, dir, &http_service_element,
+ APP_ID_HTTP, nullptr, nullptr, nullptr);
+ 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 IniServiceAPI* const init_api)
+{
+ unsigned i;
+ for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++)
+ {
+ DebugFormat(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*)
+{
+ return SERVICE_INPROCESS;
+}
+
+// End of HTTP/2 detection logic.
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// detector_http.h author Sourcefire Inc.
+
+#ifndef DETECTOR_HTTP_H
+#define DETECTOR_HTTP_H
+
+#include "detector_api.h"
+#include "http_common.h"
+
+#include "utils/util.h"
+
+struct CHPAction;
+struct CHPApp;
+struct DetectorHttpConfig;
+class AppIdConfig;
+
+#define MAX_VERSION_SIZE 64
+
+enum HttpId
+{
+ /* 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
+};
+
+struct HTTPHeaderIndices
+{
+ int start;
+ int end;
+};
+
+struct HeaderMatchedPatterns
+{
+ HTTPHeaderIndices headers[HTTP_ID_LEN];
+ int last_match;
+ int last_index_end;
+ int searched;
+};
+
+struct MatchedCHPAction
+{
+ CHPAction* mpattern;
+ int index;
+ MatchedCHPAction* next;
+};
+
+// This is an array element for the dynamically growing tally below
+struct CHPMatchCandidate
+{
+ CHPApp* chpapp;
+ int key_pattern_length_sum;
+ int key_pattern_countdown;
+};
+
+// This is a structure which will grow using realloc as needed to keep all candidates
+struct CHPMatchTally
+{
+ int allocated_elements;
+ int in_use_elements;
+ CHPMatchCandidate item[1]; // FIXIT-H: Was item[0]; must account for this in allocation and freeing.
+};
+
+int geAppidByViaPattern(const u_int8_t* data, unsigned size, char** version, const
+ DetectorHttpConfig* pHttpConfig);
+int getHTTPHeaderLocation(const uint8_t* data, unsigned size, HttpId id, int* start, int* end,
+ HeaderMatchedPatterns* hmp,
+ const DetectorHttpConfig* pHttpConfig);
+
+inline void FreeMatchedCHPActions(MatchedCHPAction* ma)
+{
+ MatchedCHPAction* tmp;
+
+ while (ma)
+ {
+ tmp = ma;
+ ma = ma->next;
+ snort_free(tmp);
+ }
+}
+
+int scanKeyCHP(PatternType ptype, char* buf, int buf_size,
+ CHPMatchTally** ppTally, MatchedCHPAction** ppmatches, const DetectorHttpConfig* pHttpConfig);
+
+AppId scanCHP(PatternType ptype, char* buf, int buf_size, MatchedCHPAction* mp,
+ char** version, char** user, char** new_field,
+ int* total_found, httpSession* hsession, Packet* p, const
+ DetectorHttpConfig* pHttpConfig);
+AppId getAppIdFromUrl(char* host, char* url, char** version,
+ char* referer, AppId* ClientAppId, AppId* serviceAppId,
+ AppId* payloadAppId, AppId* referredPayloadAppId, unsigned from_rtmp,
+ const DetectorHttpConfig* pHttpConfig);
+AppId geAppidByContentType(const uint8_t* data, int size, const
+ DetectorHttpConfig* pHttpConfig);
+AppId scan_header_x_working_with(const uint8_t* data, uint32_t size, char** version);
+void identifyUserAgent(const u_int8_t* start, int size, AppId* serviceAppId, AppId* ClientAppId,
+ char** version,
+ const DetectorHttpConfig* pHttpConfig);
+void getServerVendorVersion(const uint8_t* data, int len, char** version, char** vendor,
+ RNAServiceSubtype** subtype);
+int webdav_found(HeaderMatchedPatterns* hmp);
+int http_detector_finalize(AppIdConfig* pConfig);
+void http_detector_clean(DetectorHttpConfig* pHttpConfig);
+void finalizeFflow(fflow_info* fflow, unsigned app_type_flags, AppId target_appId,
+ Packet* p);
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// detector_imap.cc author Sourcefire Inc.
+
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include "detector_api.h"
+#include "app_info_table.h"
+#include "application_ids.h"
+#include "appid_api.h"
+#include "appid_config.h"
+#include "client_plugins/client_app_api.h"
+#include "service_plugins/service_api.h"
+
+#include "main/snort_debug.h"
+#include "search_engines/search_tool.h"
+#include "utils/util.h"
+
+/*#define DEBUG_IMAP_DETECTOR 1 */
+
+static const unsigned IMAP_USER_NAME_MAX_LEN = 32;
+static const unsigned IMAP_TAG_MAX_LEN = 6;
+static const unsigned MIN_CMDS = 3;
+
+// static const char* const OK_LOGIN = " LOGIN Ok.";
+static const char* const NO_LOGIN = " Login failed.";
+
+struct CLIENT_APP_CONFIG
+{
+ int enabled;
+};
+
+enum IMAPClientState
+{
+ 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)
+};
+
+struct ClientAppData
+{
+ 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];
+};
+
+// FIXIT-L THREAD_LOCAL?
+static CLIENT_APP_CONFIG ca_config;
+
+static CLIENT_APP_RETCODE init(const IniClientAppAPI* 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,
+ AppIdData* flowp, Packet* pkt, Detector* userData,
+ const AppIdConfig* pConfig);
+
+static RNAClientAppModule client_app_mod =
+{
+ "IMAP",
+ IpProtocol::TCP,
+ &init,
+ &clean,
+ &validate,
+ 1,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 1,
+ 0
+};
+
+struct Client_App_Pattern
+{
+ const uint8_t* pattern;
+ unsigned length;
+ int eoc;
+};
+
+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";
+
+enum Client_App_Pattern_Index
+{
+ /* order MUST correspond to that in the array, patterns[], below */
+ PATTERN_LOGIN,
+ PATTERN_AUTHENTICATE,
+ PATTERN_STARTTLS,
+ PATTERN_STARTTLS2,
+ PATTERN_IMAP_OTHER // always last
+};
+
+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 },
+};
+
+// FIXIT-L THREAD_LOCAL?
+static size_t longest_pattern;
+
+static const unsigned IMAP_PORT = 143;
+
+static const unsigned IMAP_COUNT_THRESHOLD = 2;
+
+static const char* const OK = "OK";
+static const char* const BAD = "BAD";
+static const char* const 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)
+
+// static const unsigned IMAP_MAX_BANNER = 192;
+
+enum IMAPState
+{
+ 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
+};
+
+struct ServiceIMAPData
+{
+ 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
+};
+
+static int imap_init(const IniServiceAPI* const init_api);
+static int imap_validate(ServiceValidationArgs* args);
+
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &imap_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "imap",
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &imap_validate, IMAP_PORT, IpProtocol::TCP, 0 },
+ { &imap_validate, 220, IpProtocol::TCP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+static RNAServiceValidationModule service_mod =
+{
+ "IMAP",
+ &imap_init,
+ pp,
+ nullptr,
+ nullptr,
+ 1,
+ nullptr,
+ 0
+};
+
+static const char IMAP_PATTERN[] = "* OK";
+
+struct DetectorData
+{
+ ClientAppData client;
+ ServiceIMAPData server;
+ int need_continue;
+};
+
+SO_PUBLIC RNADetectorValidationModule imap_detector_mod =
+{
+ &service_mod,
+ &client_app_mod,
+ nullptr,
+ 0,
+ nullptr
+};
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_IMAP, APPINFO_FLAG_CLIENT_USER },
+ { APP_ID_IMAPS, APPINFO_FLAG_CLIENT_USER }
+};
+
+static CLIENT_APP_RETCODE init(const IniClientAppAPI* const init_api, SF_LIST* config)
+{
+ unsigned i;
+ RNAClientAppModuleConfigItem* item;
+ SearchTool* cmd_matcher = new SearchTool("ac_full");
+
+ for (i=0; i < sizeof(patterns)/sizeof(*patterns); i++)
+ {
+ cmd_matcher->add(patterns[i].pattern, patterns[i].length, &patterns[i]);
+ if (patterns[i].length > longest_pattern)
+ longest_pattern = patterns[i].length;
+ }
+ cmd_matcher->prep();
+
+ pAppidActiveConfig->add_generic_config_element(client_app_mod.name, cmd_matcher);
+
+ ca_config.enabled = 1;
+
+ if (config)
+ {
+ SF_LNODE* cursor = nullptr;
+ for (item = (RNAClientAppModuleConfigItem*)sflist_first(config, &cursor);
+ item;
+ item = (RNAClientAppModuleConfigItem*)sflist_next(&cursor))
+ {
+ DebugFormat(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++)
+ {
+ DebugFormat(DEBUG_LOG,"registering pattern: %s\n",(const char*)patterns[i].pattern);
+ init_api->RegisterPatternNoCase(&validate, IpProtocol::TCP, patterns[i].pattern,
+ patterns[i].length, -1, init_api->pAppidConfig);
+ }
+ }
+
+ unsigned j;
+ for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++)
+ {
+ DebugFormat(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 IniServiceAPI* const init_api)
+{
+ init_api->RegisterPatternUser(&imap_validate, IpProtocol::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++)
+ {
+ DebugFormat(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)
+{
+ SearchTool* cmd_matcher =
+ (SearchTool*)clean_api->pAppidConfig->find_generic_config_element(client_app_mod.name);
+ if (cmd_matcher)
+ delete cmd_matcher;
+
+ clean_api->pAppidConfig->remove_generic_config_element(client_app_mod.name);
+}
+
+static int pattern_match(void* id, void*, int index, void* data, void*)
+{
+ Client_App_Pattern** pcmd = (Client_App_Pattern**)data;
+ unsigned long idx = (unsigned long)id;
+
+ if (index)
+ return 0;
+ pcmd = (Client_App_Pattern**)data;
+ *pcmd = &patterns[idx];
+ 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(DetectorData* dd, const uint8_t* data, uint16_t size,
+ AppIdData* 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)
+ {
+ DebugFormat(DEBUG_INSPECTOR,"%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(flowp, APP_ID_IMAPS, APP_ID_IMAPS, nullptr); // 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,
+ AppIdData* flowp, Packet*, struct Detector*,
+ const AppIdConfig* pConfig)
+{
+ const uint8_t* s = data;
+ const uint8_t* end = (data + size);
+ unsigned length;
+ Client_App_Pattern* cmd = nullptr;
+ DetectorData* dd;
+ ClientAppData* fd;
+ char tag[IMAP_TAG_MAX_LEN+1] = { 0 };
+ SearchTool* cmd_matcher =
+ (SearchTool*)( ( AppIdConfig*)pConfig)->find_generic_config_element(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 = (DetectorData*)imap_detector_mod.api->data_get(flowp, imap_detector_mod.flow_data_index);
+ if (!dd)
+ {
+ dd = (DetectorData*)snort_calloc(sizeof(DetectorData));
+ imap_detector_mod.api->data_add(flowp, dd, imap_detector_mod.flow_data_index, &snort_free);
+ 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(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 = nullptr;
+ cmd_matcher->find_all((char*)s, (length > longest_pattern ? longest_pattern : length),
+ &pattern_match, false, (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(flowp, APP_ID_IMAP, APP_ID_IMAP,
+ nullptr);
+ 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(flowp, APP_ID_IMAP, APP_ID_IMAP,
+ nullptr);
+ 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(flowp, APP_ID_IMAP, APP_ID_IMAP, nullptr);
+ 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(flowp, APP_ID_IMAP, APP_ID_IMAP, nullptr);
+ 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;
+ AppIdData* 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 = (DetectorData*)imap_detector_mod.api->data_get(flowp, imap_detector_mod.flow_data_index);
+ if (!dd)
+ {
+ dd = (DetectorData*)snort_calloc(sizeof(DetectorData));
+ imap_detector_mod.api->data_add(flowp, dd, imap_detector_mod.flow_data_index, &snort_free);
+ 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(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, nullptr, nullptr, nullptr);
+ 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, nullptr, nullptr, nullptr);
+ 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);
+ 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);
+ return SERVICE_INPROCESS;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// detector_kerberos.cc author Sourcefire Inc.
+
+#include "app_info_table.h"
+#include "application_ids.h"
+#include "client_plugins/client_app_api.h"
+#include "service_plugins/service_api.h"
+
+#include "main/snort_debug.h"
+#include "utils/util.h"
+
+/*#define DEBUG_KERBEROS 1 */
+
+enum KerberosState
+{
+ 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,
+};
+
+/*error codes from RFC 4120 */
+static const unsigned KDC_ERR_PREAUTH_FAILED = 24;
+
+#define KRB_FLAG_AUTH_FAILED 0x01
+#define KRB_FLAG_USER_DETECTED 0x02
+#define KRB_FLAG_SERVICE_DETECTED 0x04
+
+enum KRB_RETCODE
+{
+ KRB_INPROCESS,
+ KRB_FAILED,
+};
+
+struct KRBState
+{
+ 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;
+};
+
+struct KRB_CLIENT_APP_CONFIG
+{
+ int enabled;
+ int failedLogin;
+};
+
+struct DetectorData
+{
+ KRBState clnt_state;
+ KRBState svr_state;
+ int set_flags;
+ int need_continue;
+};
+
+// FIXIT-L THREAD_LOCAL?
+static KRB_CLIENT_APP_CONFIG krb_client_config;
+
+static CLIENT_APP_RETCODE krb_client_init(const IniClientAppAPI* const init_api, SF_LIST* config);
+static CLIENT_APP_RETCODE krb_client_validate(const uint8_t* data, uint16_t size, const int dir,
+ AppIdData* flowp, Packet* pkt, struct Detector* userData,
+ const AppIdConfig* pConfig);
+
+static RNAClientAppModule client_app_mod =
+{
+ "KERBEROS",
+ IpProtocol::UDP,
+ &krb_client_init,
+ nullptr,
+ &krb_client_validate,
+ 1,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 1,
+ 0
+};
+
+struct Detector_Pattern
+{
+ const uint8_t* pattern;
+ unsigned length;
+};
+
+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 IniServiceAPI* const init_api);
+static int krb_server_validate(ServiceValidationArgs* args);
+
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &krb_server_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "kerberos"
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &krb_server_validate, 88, IpProtocol::TCP, 0 },
+ { &krb_server_validate, 88, IpProtocol::UDP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+static RNAServiceValidationModule service_mod =
+{
+ "KRB",
+ &krb_server_init,
+ pp,
+ nullptr,
+ nullptr,
+ 1,
+ nullptr,
+ 0
+};
+
+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 },
+};
+
+SO_PUBLIC RNADetectorValidationModule kerberos_detector_mod =
+{
+ &service_mod,
+ &client_app_mod,
+ nullptr,
+ 0,
+ nullptr
+};
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_KERBEROS, APPINFO_FLAG_CLIENT_USER | APPINFO_FLAG_SERVICE_ADDITIONAL }
+};
+
+static CLIENT_APP_RETCODE krb_client_init(const IniClientAppAPI* const init_api, SF_LIST* config)
+{
+ unsigned i;
+ RNAClientAppModuleConfigItem* item;
+
+ krb_client_config.enabled = 1;
+ krb_client_config.failedLogin = 0;
+
+ if (config)
+ {
+ SF_LNODE* iter = nullptr;
+
+ for (item = (RNAClientAppModuleConfigItem*)sflist_first(config, &iter);
+ item;
+ item = (RNAClientAppModuleConfigItem*)sflist_next(&iter))
+ {
+ DebugFormat(DEBUG_INSPECTOR,"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++)
+ {
+ DebugFormat(DEBUG_INSPECTOR,"registering pattern with length %u\n",
+ client_patterns[i].length);
+ init_api->RegisterPattern(&krb_client_validate, IpProtocol::UDP,
+ client_patterns[i].pattern, client_patterns[i].length, -1, init_api->pAppidConfig);
+ init_api->RegisterPattern(&krb_client_validate, IpProtocol::TCP,
+ client_patterns[i].pattern, client_patterns[i].length, -1, init_api->pAppidConfig);
+ }
+ }
+
+ unsigned j;
+ for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++)
+ {
+ DebugFormat(DEBUG_INSPECTOR,"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 IniServiceAPI* const init_api)
+{
+ unsigned i;
+
+ for (i=0; i < sizeof(service_patterns)/sizeof(*service_patterns); i++)
+ {
+ DebugFormat(DEBUG_INSPECTOR,"registering pattern with length %u\n",
+ service_patterns[i].length);
+ init_api->RegisterPatternUser(&krb_server_validate, IpProtocol::UDP,
+ service_patterns[i].pattern,
+ service_patterns[i].length, -1, "kerberos", init_api->pAppidConfig);
+ init_api->RegisterPatternUser(&krb_server_validate, IpProtocol::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++)
+ {
+ DebugFormat(DEBUG_INSPECTOR,"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,
+ AppIdData* flowp)
+{
+ 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)
+ {
+ DebugFormat(DEBUG_INSPECTOR,"%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
+ DebugFormat(DEBUG_INSPECTOR,"%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
+ DebugFormat(DEBUG_INSPECTOR,"%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
+ DebugFormat(DEBUG_INSPECTOR,"%p Valid\n",flowp);
+#endif
+ if (!krbs->added)
+ {
+ client_app_mod.api->add_app(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
+ DebugFormat(DEBUG_INSPECTOR,"%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
+ DebugFormat(DEBUG_INSPECTOR,"%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,
+ AppIdData* flowp, Packet* 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)
+ {
+ DebugFormat(DEBUG_INSPECTOR,"%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
+ DebugFormat(DEBUG_INSPECTOR,"%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
+ DebugFormat(DEBUG_INSPECTOR,"%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
+ DebugFormat(DEBUG_INSPECTOR,"%p unAuthorized\n",flowp);
+#endif
+ krbs->flags |= KRB_FLAG_AUTH_FAILED;
+ }
+ krbs->state = KRB_STATE_FIELD;
+ break;
+ case KRB_STATE_FIELD:
+#ifdef DEBUG_KERBEROS
+ DebugFormat(DEBUG_INSPECTOR,"%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
+ DebugFormat(DEBUG_INSPECTOR,"%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
+ DebugFormat(DEBUG_INSPECTOR,"%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,
+ nullptr, krbs->ver, nullptr);
+ 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,
+ AppIdData* flowp, Packet*, struct Detector*, const AppIdConfig*)
+{
+ const uint8_t* s = data;
+ const uint8_t* end = (data + size);
+ DetectorData* fd;
+
+#ifdef DEBUG_KERBEROS
+ DebugFormat(DEBUG_INSPECTOR, "%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 = (DetectorData*)kerberos_detector_mod.api->data_get(flowp,
+ kerberos_detector_mod.flow_data_index);
+ if (!fd)
+ {
+ fd = (DetectorData*)snort_calloc(sizeof(DetectorData));
+ kerberos_detector_mod.api->data_add(flowp, fd,
+ kerberos_detector_mod.flow_data_index, &snort_free);
+ if (flowp->proto == IpProtocol::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) == KRB_FAILED)
+ {
+#ifdef DEBUG_KERBEROS
+ DebugFormat(DEBUG_INSPECTOR,"%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, nullptr, dir,
+ fd->clnt_state.cname) == KRB_FAILED)
+ {
+#ifdef DEBUG_KERBEROS
+ DebugFormat(DEBUG_INSPECTOR,"%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;
+ AppIdData* flowp = args->flowp;
+ const uint8_t* data = args->data;
+ Packet* 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
+ DebugFormat(DEBUG_INSPECTOR, "%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 = (DetectorData*)kerberos_detector_mod.api->data_get(flowp,
+ kerberos_detector_mod.flow_data_index);
+ if (!fd)
+ {
+ fd = (DetectorData*)snort_calloc(sizeof(DetectorData));
+ kerberos_detector_mod.api->data_add(flowp, fd,
+ kerberos_detector_mod.flow_data_index, &snort_free);
+ if (flowp->proto == IpProtocol::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
+ DebugFormat(DEBUG_INSPECTOR,"%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);
+ return SERVICE_NOMATCH;
+ }
+ clearAppIdFlag(flowp, APPID_SESSION_CONTINUE);
+ return SERVICE_SUCCESS;
+ }
+
+inprocess:
+ service_mod.api->service_inprocess(flowp, pkt, dir, &svc_element);
+ return SERVICE_INPROCESS;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// detector_pattern.cc author Sourcefire Inc.
+
+#include "detector_pattern.h"
+#include "app_info_table.h"
+#include "client_plugins/client_app_api.h"
+#include "service_plugins/service_api.h"
+
+#include "log/messages.h"
+#include "main/snort_debug.h"
+#include "utils/util.h"
+
+static int service_validate(ServiceValidationArgs* args);
+static int csdPatternTreeSearch(const uint8_t* data, uint16_t size, IpProtocol protocol,
+ Packet* pkt,
+ const RNAServiceElement** serviceData, bool isClient,
+ const AppIdConfig* pConfig);
+static int pattern_service_init(const IniServiceAPI* const iniServiceApi);
+static void pattern_service_clean(const CleanServiceAPI* const clean_api);
+static CLIENT_APP_RETCODE client_init(const IniClientAppAPI* const init_api, SF_LIST* config);
+static CLIENT_APP_RETCODE client_init_tcp(const IniClientAppAPI* const init_api, SF_LIST* config);
+static CLIENT_APP_RETCODE client_validate(const uint8_t* data, uint16_t size, const int dir,
+ AppIdData* flowp, Packet* pkt, struct Detector* userData,
+ const AppIdConfig* pConfig);
+static void client_clean(const CleanClientAppAPI* const clean_api);
+static const IniServiceAPI* iniServiceApi;
+static const IniClientAppAPI* iniClientApi;
+
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &service_validate,
+ nullptr,
+ DETECTOR_TYPE_PATTERN,
+ 1,
+ 1,
+ 0,
+ "pattern",
+};
+
+RNAServiceValidationModule pattern_service_mod =
+{
+ "pattern",
+ &pattern_service_init,
+ nullptr,
+ nullptr,
+ nullptr,
+ 0,
+ &pattern_service_clean,
+ 0
+};
+
+// client side
+RNAClientAppModule pattern_udp_client_mod =
+{
+ "pattern",
+ IpProtocol::UDP,
+ &client_init,
+ &client_clean,
+ &client_validate,
+ 0,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ 0
+};
+
+RNAClientAppModule pattern_tcp_client_mod =
+{
+ "pattern",
+ IpProtocol::TCP,
+ &client_init_tcp,
+ nullptr,
+ &client_validate,
+ 0,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ 0
+};
+
+static RNAServiceValidationPort pp =
+{
+ &service_validate,
+ 0,
+ IpProtocol::PROTO_NOT_SET,
+ 0
+};
+
+static void FreePattern(Pattern* pattern)
+{
+ if (pattern)
+ {
+ if (pattern->data)
+ snort_free(pattern->data);
+ snort_free(pattern);
+ }
+}
+
+static void FreePatternService(PatternService* ps)
+{
+ Pattern* pattern;
+ PortNode* port;
+
+ if (ps)
+ {
+ while ((pattern = ps->pattern))
+ {
+ ps->pattern = pattern->next;
+ FreePattern(pattern);
+ }
+ while ((port = ps->port))
+ {
+ ps->port = port->next;
+ snort_free(port);
+ }
+ snort_free(ps);
+ }
+}
+
+static void read_patterns(PortPatternNode* portPatternList, PatternService** serviceList)
+{
+ PatternService* ps = nullptr;
+ Pattern* pattern;
+ PortNode* port;
+ PortPatternNode* pNode;
+ char* lastName = nullptr;
+ short lastPort = 0;
+ IpProtocol lastProto = IpProtocol::PROTO_NOT_SET;
+ bool newPs;
+
+ for (pNode = portPatternList; pNode; pNode = pNode->next)
+ {
+ newPs = false;
+ if (!ps || !lastName || strcmp(lastName, pNode->detectorName)
+ || lastProto != pNode->protocol)
+ {
+ ps = (PatternService*)snort_calloc(sizeof(PatternService));
+ 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))
+ {
+ port = (PortNode*)snort_calloc(sizeof(PortNode));
+ port->port = pNode->port;
+ port->next = ps->port;
+ lastPort = pNode->port;
+ ps->port = port;
+ }
+
+ pattern = (Pattern*)snort_calloc(sizeof(Pattern));
+ pattern->data = (uint8_t*)snort_calloc(pNode->length);
+ 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 IniServiceAPI* const iniServiceApi)
+{
+ PatternService* ps;
+ PortNode* 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 = (IpProtocol)ps->proto;
+ if (iniServiceApi->AddPort(&pp, &pattern_service_mod, iniServiceApi->pAppidConfig))
+ ErrorMessage("Failed to add port - %d:%u:%d\n",ps->id,
+ (unsigned)pp.port, (uint8_t)pp.proto);
+ else
+ {
+ DebugFormat(DEBUG_LOG,"Installed ports - %d:%u:%u\n",
+ ps->id, (unsigned)pp.port, (unsigned)pp.proto);
+ }
+ }
+ }
+}
+
+static void RegisterPattern(SearchTool** patterns, Pattern* pattern)
+{
+ if (!*patterns)
+ {
+ *patterns = new SearchTool("ac_full");
+ if (!*patterns)
+ {
+ ErrorMessage("Error initializing the pattern table\n");
+ return;
+ }
+ }
+
+ (*patterns)->add((char*)pattern->data, pattern->length, pattern, false);
+}
+
+// Creates unique subset of services registered on ports, and then creates pattern trees.
+static void createServicePatternTrees(AppIdConfig* pConfig)
+{
+ PatternService* ps;
+ Pattern* pattern;
+ PortNode* 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 == IpProtocol::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 != IpProtocol::TCP))
+ continue;
+
+ for (pattern = ps->pattern; pattern; pattern = pattern->next)
+ RegisterPattern(&pConfig->servicePortPattern->tcpPortPatternTree[i], pattern);
+ }
+
+ pConfig->servicePortPattern->tcpPortPatternTree[i]->prep();
+ }
+ if (pConfig->servicePortPattern->udpPortPatternTree[i])
+ {
+ for (ps = pConfig->servicePortPattern->servicePortPattern; ps; ps = ps->next)
+ {
+ if (ps->port || (ps->proto != IpProtocol::UDP))
+ continue;
+
+ for (pattern = ps->pattern; pattern; pattern = pattern->next)
+ RegisterPattern(&pConfig->servicePortPattern->udpPortPatternTree[i], pattern);
+ }
+
+ pConfig->servicePortPattern->udpPortPatternTree[i]->prep();
+ }
+ }
+}
+
+static void createClientPatternTrees(
+ AppIdConfig* 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 == IpProtocol::TCP)
+ RegisterPattern(&pConfig->clientPortPattern->tcp_patterns, pattern);
+ else
+ RegisterPattern(&pConfig->clientPortPattern->udp_patterns, pattern);
+ }
+ }
+}
+
+void registerServicePatterns(
+ AppIdConfig* 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 == IpProtocol::TCP)
+ {
+ DebugFormat(DEBUG_LOG,"Adding pattern with length %u\n",pattern->length);
+ iniServiceApi->RegisterPattern(&service_validate, IpProtocol::TCP,
+ pattern->data, pattern->length,
+ pattern->offset, "pattern", iniServiceApi->pAppidConfig);
+ RegisterPattern(&pConfig->servicePortPattern->tcp_patterns, pattern);
+ }
+ else
+ {
+ DebugFormat(DEBUG_LOG,"Adding pattern with length %u\n",pattern->length);
+ iniServiceApi->RegisterPattern(&service_validate, IpProtocol::UDP,
+ pattern->data, pattern->length,
+ pattern->offset, "pattern", iniServiceApi->pAppidConfig);
+ RegisterPattern(&pConfig->servicePortPattern->udp_patterns, pattern);
+ }
+ }
+ }
+ }
+ else
+ {
+ for (pattern = ps->pattern; pattern; pattern = pattern->next)
+ ps->count++;
+ }
+ }
+ if (pConfig->servicePortPattern->tcp_patterns)
+ {
+ pConfig->servicePortPattern->tcp_patterns->prep();
+ }
+ if (pConfig->servicePortPattern->udp_patterns)
+ {
+ pConfig->servicePortPattern->udp_patterns->prep();
+ }
+}
+
+void registerClientPatterns(
+ AppIdConfig* 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 == IpProtocol::TCP)
+ {
+ DebugFormat(DEBUG_LOG,"Adding pattern with length %u\n",pattern->length);
+ iniClientApi->RegisterPattern(&client_validate, IpProtocol::TCP, pattern->data,
+ pattern->length,
+ pattern->offset, iniClientApi->pAppidConfig);
+ RegisterPattern(&pConfig->clientPortPattern->tcp_patterns, pattern);
+ }
+ else
+ {
+ DebugFormat(DEBUG_LOG,"Adding pattern with length %u\n",pattern->length);
+ iniClientApi->RegisterPattern(&client_validate, IpProtocol::UDP, pattern->data,
+ pattern->length, pattern->offset, iniClientApi->pAppidConfig);
+ RegisterPattern(&pConfig->clientPortPattern->udp_patterns, pattern);
+ }
+ }
+ ps->count++;
+ }
+ }
+ if (pConfig->clientPortPattern->tcp_patterns)
+ {
+ pConfig->clientPortPattern->tcp_patterns->prep();
+ }
+ if (pConfig->clientPortPattern->udp_patterns)
+ {
+ pConfig->clientPortPattern->udp_patterns->prep();
+ }
+}
+
+void dumpPatterns(const char* name, PatternService* pList)
+{
+ UNUSED(name);
+ 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.
+ */
+
+ DebugFormat(DEBUG_LOG,"Adding pattern for \"%s\"\n",name);
+ for (ps = pList; ps; ps = ps->next)
+ {
+ for (pattern = ps->pattern; pattern; pattern = pattern->next)
+ {
+ DebugFormat(DEBUG_LOG,"\t%s, %d\n",pattern->data, pattern->length);
+ if (pattern->data && pattern->length)
+ {
+ DebugFormat(DEBUG_LOG,"\t\t%s, %d\n",pattern->data, pattern->length);
+ }
+ }
+ }
+}
+
+int portPatternFinalize(AppIdConfig* 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, iniServiceApi);
+ createServicePatternTrees(pConfig);
+ registerServicePatterns(pConfig);
+ dumpPatterns("Server", pConfig->servicePortPattern->servicePortPattern);
+ }
+
+ return 0;
+}
+
+static int pattern_service_init(const IniServiceAPI* const init_api)
+{
+ iniServiceApi = init_api;
+
+ DebugFormat(DEBUG_LOG,"Initializing with instance %u\n",iniServiceApi->instance_id);
+
+ return 0;
+}
+
+static void pattern_service_clean(const CleanServiceAPI* const clean_api)
+{
+ PatternService* ps;
+ AppIdConfig* pConfig = clean_api->pAppidConfig;
+
+ if (pConfig->servicePortPattern && pConfig->servicePortPattern->servicePortPattern)
+ {
+ unsigned i;
+
+ if (pConfig->servicePortPattern->tcp_patterns)
+ {
+ delete pConfig->servicePortPattern->tcp_patterns;
+ pConfig->servicePortPattern->tcp_patterns = nullptr;
+ }
+ if (pConfig->servicePortPattern->udp_patterns)
+ {
+ delete pConfig->servicePortPattern->udp_patterns;
+ pConfig->servicePortPattern->udp_patterns = nullptr;
+ }
+ for (i = 0; i < 65536; i++)
+ {
+ if (pConfig->servicePortPattern->tcpPortPatternTree[i])
+ {
+ delete pConfig->servicePortPattern->tcpPortPatternTree[i];
+ pConfig->servicePortPattern->tcpPortPatternTree[i] = nullptr;
+ }
+ if (pConfig->servicePortPattern->udpPortPatternTree[i])
+ {
+ delete pConfig->servicePortPattern->udpPortPatternTree[i];
+ pConfig->servicePortPattern->udpPortPatternTree[i] = nullptr;
+ }
+ }
+ while (pConfig->servicePortPattern->servicePortPattern)
+ {
+ ps = pConfig->servicePortPattern->servicePortPattern;
+ pConfig->servicePortPattern->servicePortPattern = ps->next;
+ FreePatternService(ps);
+ }
+ }
+}
+
+struct PServiceMatch
+{
+ /**Matches are aggregated by PatternService first and then by patterns. next is used to walk
+ matches by PatternService*/
+ PServiceMatch* next;
+
+ /**Walks matches by pattern within a PatternService. */
+ PServiceMatch* ps_next;
+
+ Pattern* data;
+};
+
+static PServiceMatch* free_servicematch_list;
+
+static int pattern_match(void* id, void*, int index, void* data, void*)
+{
+ 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
+ sm = (PServiceMatch*)snort_calloc(sizeof(PServiceMatch));
+
+ 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
+ sm = (PServiceMatch*)snort_calloc(sizeof(PServiceMatch));
+
+ sm->data = pd;
+ sm->next = *matches;
+ *matches = sm;
+ return 0;
+}
+
+static int csdPatternTreeSearch(const uint8_t* data, uint16_t size, IpProtocol protocol,
+ Packet* pkt,
+ const RNAServiceElement** serviceData, bool isClient,
+ const AppIdConfig* pConfig)
+{
+ SearchTool* patternTree = nullptr;
+ PatternService* ps;
+ PServiceMatch* matches = nullptr;
+ PServiceMatch* sm;
+ PServiceMatch* psm;
+ Pattern* pattern;
+
+ if (!data || !pkt || !size)
+ return 0;
+
+ *serviceData = nullptr;
+
+ if (!isClient)
+ {
+ if (protocol == IpProtocol::UDP)
+ patternTree = pConfig->servicePortPattern->udpPortPatternTree[pkt->ptrs.sp];
+ else
+ patternTree = pConfig->servicePortPattern->tcpPortPatternTree[pkt->ptrs.sp];
+ }
+
+ if (!patternTree)
+ {
+ if (protocol == IpProtocol::UDP)
+ patternTree = (isClient) ? pConfig->clientPortPattern->udp_patterns :
+ pConfig->servicePortPattern->udp_patterns;
+ else
+ patternTree = (isClient) ? pConfig->clientPortPattern->tcp_patterns :
+ pConfig->servicePortPattern->tcp_patterns;
+ }
+
+ if (patternTree)
+ {
+ patternTree->find_all((char*)data, size, &pattern_match, false, (void*)&matches);
+ }
+
+ if (matches == nullptr)
+ return 0;
+
+ /* match highest count and then longest pattern. */
+ ps = nullptr;
+ 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 == nullptr)
+ break;
+ }
+
+ if (pattern == nullptr) /*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 == nullptr)
+ return 0;
+ *serviceData = &svc_element;
+ return ps->id;
+}
+
+static int service_validate(ServiceValidationArgs* args)
+{
+ uint32_t id;
+ const RNAServiceElement* service = nullptr;
+ AppIdData* flowp = args->flowp;
+ const uint8_t* data = args->data;
+ Packet* 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, nullptr, nullptr,
+ nullptr);
+ return SERVICE_SUCCESS;
+
+inprocess:
+ pattern_service_mod.api->service_inprocess(flowp, pkt, dir, &svc_element);
+ return SERVICE_INPROCESS;
+
+fail:
+ pattern_service_mod.api->fail_service(flowp, pkt, dir, &svc_element,
+ pattern_service_mod.flow_data_index,
+ args->pConfig);
+ return SERVICE_NOMATCH;
+}
+
+static CLIENT_APP_RETCODE client_init(const IniClientAppAPI* const init_api, SF_LIST*)
+{
+ iniClientApi = init_api;
+
+ return CLIENT_APP_SUCCESS;
+}
+
+static CLIENT_APP_RETCODE client_init_tcp(const IniClientAppAPI* const, SF_LIST*)
+{
+ return CLIENT_APP_SUCCESS;
+}
+
+static void client_clean(const CleanClientAppAPI* const clean_api)
+{
+ AppIdConfig* pConfig = clean_api->pAppidConfig;
+
+ if (pConfig->clientPortPattern && pConfig->clientPortPattern->servicePortPattern)
+ {
+ if (pConfig->clientPortPattern->tcp_patterns)
+ {
+ delete pConfig->clientPortPattern->tcp_patterns;
+ pConfig->clientPortPattern->tcp_patterns = nullptr;
+ }
+
+ if (pConfig->clientPortPattern->udp_patterns)
+ {
+ delete pConfig->clientPortPattern->udp_patterns;
+ pConfig->clientPortPattern->udp_patterns = nullptr;
+ }
+ }
+}
+
+static CLIENT_APP_RETCODE client_validate(const uint8_t* data, uint16_t size, const int dir,
+ AppIdData* flowp, Packet* pkt, struct Detector*, const AppIdConfig* pConfig)
+{
+ AppId id;
+ const RNAServiceElement* service = nullptr;
+
+ 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,
+ (AppIdConfig*)pConfig);
+ if (!id)
+ goto fail;
+
+ pattern_tcp_client_mod.api->add_app(flowp, id, id, nullptr);
+ return CLIENT_APP_SUCCESS;
+
+inprocess:
+ return CLIENT_APP_INPROCESS;
+
+fail:
+ return CLIENT_APP_EINVALID;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// detector_pattern.h author Sourcefire Inc.
+
+#ifndef DETECTOR_PATTERN_H
+#define DETECTOR_PATTERN_H
+
+#include "appid_api.h"
+#include "detector_api.h"
+
+extern RNAServiceValidationModule pattern_service_mod;
+
+struct PortPatternNode
+{
+ AppId appId;
+ IpProtocol protocol;
+ unsigned short port;
+ unsigned char* pattern;
+ unsigned length;
+ int32_t offset;
+ char* detectorName;
+ PortPatternNode* next;
+};
+
+struct PatternService;
+struct Pattern
+{
+ Pattern* next;
+ unsigned length;
+ int offset;
+ uint8_t* data;
+ PatternService* ps;
+};
+
+struct PortNode // FIXIT this name changed from "Port" which is already in use by Snort++
+{
+ PortNode* next;
+ uint16_t port;
+};
+
+/**list for pattern services. Each pattern service is unique for a given uuid. */
+struct PatternService
+{
+ PatternService* next;
+ AppId id;
+ Pattern* pattern;
+ PortNode* port;
+ IpProtocol proto;
+ unsigned count;
+ unsigned longest;
+};
+
+class SearchTool;
+struct ServicePortPattern
+{
+ PortPatternNode* luaInjectedPatterns;
+ PatternService* servicePortPattern;
+ SearchTool* tcp_patterns;
+ SearchTool* udp_patterns;
+ SearchTool* tcpPortPatternTree[65536];
+ SearchTool* udpPortPatternTree[65536];
+};
+
+struct ClientPortPattern
+{
+ PortPatternNode* luaInjectedPatterns;
+ PatternService* servicePortPattern;
+ SearchTool* tcp_patterns;
+ SearchTool* udp_patterns;
+};
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// detector_pop3.cc author Sourcefire Inc.
+
+#include "app_info_table.h"
+#include "application_ids.h"
+#include "client_plugins/client_app_api.h"
+#include "detector_plugins/detector_api.h"
+#include "service_plugins/service_api.h"
+#include "service_plugins/service_util.h"
+
+#include "log/messages.h"
+#include "main/snort_debug.h"
+#include "search_engines/search_tool.h"
+#include "utils/util.h"
+
+/*#define DEBUG_POP3 1 */
+
+struct POP3_CLIENT_APP_CONFIG
+{
+ int enabled;
+};
+
+enum POP3ClientState
+{
+ 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)
+};
+
+struct ClientPOP3Data
+{
+ int auth;
+ char* username;
+ POP3ClientState state;
+ int set_flags;
+ int detected;
+ int got_user;
+};
+
+// static const unsigned MIN_POP3_CMDS = 3;
+
+// FIXIT-L THREAD_LOCAL?
+static POP3_CLIENT_APP_CONFIG pop3_config;
+
+static CLIENT_APP_RETCODE pop3_ca_init(const IniClientAppAPI* 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,
+ AppIdData* flowp, Packet* pkt, struct Detector* userData,
+ const AppIdConfig* pConfig);
+
+static RNAClientAppModule client_app_mod =
+{
+ "POP3",
+ IpProtocol::TCP,
+ &pop3_ca_init,
+ &pop3_ca_clean,
+ &pop3_ca_validate,
+ 1,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 1,
+ 0
+};
+
+struct Client_App_Pattern
+{
+ const uint8_t* pattern;
+ unsigned length;
+ int eoc;
+};
+
+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";
+
+enum Client_App_Pattern_Index
+{
+ /* 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_POP3_OTHER // always last
+};
+
+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 },
+ /* 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 },
+};
+
+// FIXIT-L THREAD_LOCAL?
+static size_t longest_pattern;
+
+static const unsigned POP3_PORT = 110;
+
+static const unsigned POP3_COUNT_THRESHOLD = 4;
+
+static const char* const POP3_OK = "+OK";
+static const char* const POP3_ERR = "-ERR";
+static const char* const POP3_TERM = ".\x00D\x00A";
+
+enum POP3State
+{
+ POP3_STATE_CONNECT,
+ POP3_STATE_RESPONSE,
+ POP3_STATE_CONTINUE
+};
+
+static const unsigned MAX_VERSION_SIZE = 64;
+struct ServicePOP3Data
+{
+ POP3State state;
+ unsigned count;
+ const char* vendor;
+ char version[MAX_VERSION_SIZE];
+ RNAServiceSubtype* subtype;
+ int error;
+};
+
+static int pop3_init(const IniServiceAPI* const init_api);
+static int pop3_validate(ServiceValidationArgs* args);
+
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &pop3_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "pop3"
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &pop3_validate, POP3_PORT, IpProtocol::TCP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+static RNAServiceValidationModule service_mod =
+{
+ "POP3",
+ &pop3_init,
+ pp,
+ nullptr,
+ nullptr,
+ 1,
+ nullptr,
+ 0
+};
+
+struct POP3DetectorData
+{
+ ClientPOP3Data client;
+ ServicePOP3Data server;
+ int need_continue;
+};
+
+SO_PUBLIC RNADetectorValidationModule pop3_detector_mod =
+{
+ &service_mod,
+ &client_app_mod,
+ nullptr,
+ 0,
+ nullptr
+};
+
+static AppRegistryEntry 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 IniClientAppAPI* const init_api, SF_LIST* config)
+{
+ unsigned i;
+ RNAClientAppModuleConfigItem* item;
+ SearchTool* cmd_matcher = new SearchTool("ac_full");
+
+ for (i=0; i < sizeof(patterns)/sizeof(*patterns); i++)
+ {
+ cmd_matcher->add(patterns[i].pattern, patterns[i].length, &patterns[i]);
+ if (patterns[i].length > longest_pattern)
+ longest_pattern = patterns[i].length;
+ }
+ cmd_matcher->prep();
+
+ init_api->pAppidConfig->add_generic_config_element(client_app_mod.name, cmd_matcher);
+
+ pop3_config.enabled = 1;
+
+ if (config)
+ {
+ SF_LNODE* iter = nullptr;
+
+ for (item = (RNAClientAppModuleConfigItem*)sflist_first(config, &iter);
+ item;
+ item = (RNAClientAppModuleConfigItem*)sflist_next(&iter))
+ {
+ DebugFormat(DEBUG_INSPECTOR,"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++)
+ {
+ DebugFormat(DEBUG_INSPECTOR,"registering pattern: %s\n",
+ (const char*)patterns[i].pattern);
+ init_api->RegisterPatternNoCase(&pop3_ca_validate, IpProtocol::TCP,
+ patterns[i].pattern,
+ patterns[i].length, 0, init_api->pAppidConfig);
+ }
+ }
+
+ unsigned j;
+ for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++)
+ {
+ DebugFormat(DEBUG_INSPECTOR,"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 IniServiceAPI* const init_api)
+{
+ init_api->RegisterPatternUser(&pop3_validate, IpProtocol::TCP, (uint8_t*)POP3_OK,
+ sizeof(POP3_OK)-1, 0, "pop3", init_api->pAppidConfig);
+ init_api->RegisterPatternUser(&pop3_validate, IpProtocol::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++)
+ {
+ DebugFormat(DEBUG_INSPECTOR,"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)
+{
+ SearchTool* cmd_matcher =
+ (SearchTool*)clean_api->pAppidConfig->find_generic_config_element(client_app_mod.name);
+ if (cmd_matcher)
+ delete cmd_matcher;
+ clean_api->pAppidConfig->remove_generic_config_element(client_app_mod.name);
+}
+
+static int pop3_pattern_match(void* id, void*, int index, void* data, void*)
+{
+ Client_App_Pattern** pcmd;
+ unsigned long idx = (unsigned long)id;
+
+ if (index)
+ return 0;
+ pcmd = (Client_App_Pattern**)data;
+ *pcmd = &patterns[idx];
+ 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)
+ snort_free((void*)sub->service);
+ if (sub->version)
+ snort_free((void*)sub->version);
+ snort_free(sub);
+ }
+ cd = &dd->client;
+ if (cd->username)
+ snort_free(cd->username);
+ snort_free(dd);
+ }
+}
+
+static int pop3_check_line(const uint8_t** data, const uint8_t* end)
+{
+ /* Line in the form (textCRLF) */
+ for (; (*data)<end; (*data)++)
+ {
+ if (**data == 0x0D)
+ {
+ (*data)++;
+ if (*data < end && **data == 0x0A)
+ {
+ (*data)++;
+ return 0;
+ }
+ return -1;
+ }
+ else if (!isprint(**data))
+ return -1;
+ }
+ return 1;
+}
+
+static int pop3_server_validate(POP3DetectorData* dd, const uint8_t* data, uint16_t size,
+ AppIdData* flowp, int server)
+{
+ static const char ven_cppop[] = "cppop";
+ static const char ven_cc[] = "Cubic Circle";
+ static const char ven_im[] = "InterMail";
+ static const char ver_cc[] = "'s v";
+ static const char ven_po[] = "Post.Office";
+ static const char ver_po[] = " v";
+ static const char ver_po2[] = " release ";
+ static const char sub_po[] = " with ";
+ static const char subver_po[] = " version ";
+ ServicePOP3Data* pd = &dd->server;
+ const uint8_t* begin = nullptr;
+ 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 = nullptr;
+ 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(flowp, APP_ID_POP3S, APP_ID_POP3S, nullptr); // 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);
+ snort_free(dd->client.username);
+ dd->client.username = nullptr;
+ }
+ else
+ {
+ service_mod.api->add_user(flowp, dd->client.username, APP_ID_POP3, 1);
+ snort_free(dd->client.username);
+ dd->client.username = nullptr;
+ dd->need_continue = 0;
+ clearAppIdFlag(flowp, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS);
+ dd->client.got_user = 1;
+ if (dd->client.detected)
+ setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED);
+ }
+ }
+ 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 = (RNAServiceSubtype*)snort_calloc(sizeof(RNAServiceSubtype));
+ unsigned sub_len;
+
+ sub_len = p - s;
+ sub->service = (const char*)snort_calloc(sub_len+1);
+ 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 = (const char*)snort_calloc(sub_len+1);
+ memcpy((char*)sub->version, s, sub_len);
+ ((char*)sub->version)[sub_len] = 0;
+ }
+ }
+ }
+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,
+ AppIdData* flowp, Packet*, struct Detector*, const AppIdConfig* 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 = (POP3DetectorData*)pop3_detector_mod.api->data_get(flowp,
+ pop3_detector_mod.flow_data_index);
+ if (!dd)
+ {
+ dd = (POP3DetectorData*)snort_calloc(sizeof(POP3DetectorData));
+ pop3_detector_mod.api->data_add(flowp, dd,
+ pop3_detector_mod.flow_data_index, &pop3_free_state);
+ 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
+ DebugFormat(DEBUG_INSPECTOR,"%p Calling server\n",flowp);
+ DumpHex(SF_DEBUG_FILE, data, size);
+#endif
+
+ if (pop3_server_validate(dd, data, size, flowp, 0))
+ clearAppIdFlag(flowp, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS);
+ return CLIENT_APP_INPROCESS;
+ }
+
+#ifdef DEBUG_POP3
+ DebugFormat(DEBUG_INSPECTOR,"%p Client\n",flowp);
+ DumpHex(SF_DEBUG_FILE, data, size);
+#endif
+
+ while ((length = (end - s)))
+ {
+ unsigned pattern_index;
+ SearchTool* cmd_matcher =
+ (SearchTool*)((AppIdConfig*)pConfig)->find_generic_config_element(client_app_mod.name);
+
+ cmd = nullptr;
+ cmd_matcher->find_all((char*)s, (length > longest_pattern ? longest_pattern : length),
+ &pop3_pattern_match, false, (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:
+ {
+ /* 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)
+ snort_free(fd->username);
+ fd->username = snort_strdup(username);
+ }
+ 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<space> 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<space> 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(flowp, APP_ID_POP3, APP_ID_POP3, nullptr); // 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;
+ AppIdData* flowp = args->flowp;
+ const uint8_t* data = args->data;
+ Packet* 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
+ DebugFormat(DEBUG_INSPECTOR,"%p Dir %d\n",flowp, dir);
+ DumpHex(SF_DEBUG_FILE, data, size);
+#endif
+
+ dd = (POP3DetectorData*)pop3_detector_mod.api->data_get(flowp,
+ pop3_detector_mod.flow_data_index);
+ if (!dd)
+ {
+ dd = (POP3DetectorData*)snort_calloc(sizeof(POP3DetectorData));
+ pop3_detector_mod.api->data_add(flowp, dd,
+ pop3_detector_mod.flow_data_index, &pop3_free_state);
+ 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))
+ {
+ 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 : nullptr, pd->subtype);
+ pd->subtype = nullptr;
+ 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);
+ return SERVICE_NOMATCH;
+ }
+ else
+ {
+ clearAppIdFlag(flowp, APPID_SESSION_CONTINUE);
+ return SERVICE_SUCCESS;
+ }
+
+inprocess:;
+ service_mod.api->service_inprocess(flowp, pkt, dir, &svc_element);
+ return SERVICE_INPROCESS;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// detector_sip.cc author Sourcefire Inc.
+
+#include "detector_sip.h"
+
+#include "http_url_patterns.h"
+#include "fw_appid.h"
+#include "service_plugins/service_base.h"
+#include "util/sf_mlmp.h"
+#include "utils/util.h"
+
+#include "log/messages.h"
+#include "main/snort_debug.h"
+#include "service_inspectors/sip/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";
+static const unsigned SIP_PORT = 5060;
+
+// static const unsigned MAX_ADDRESS_SIZE = 16;
+// static const unsigned MAX_CALLID_SIZE = 64;
+static const unsigned MAX_VENDOR_SIZE = 64;
+// static const unsigned MAX_PORT_SIZE = 6;
+
+enum SIPState
+{
+ SIP_STATE_INIT=0,
+ SIP_STATE_REGISTER,
+ SIP_STATE_CALL
+};
+
+// static const unsigned SIP_STATUS_OK = 200;
+
+// static const unsigned SIP_MAX_INFO_SIZE = 63;
+
+enum tSIP_FLAGS
+{
+ SIP_FLAG_SERVER_CHECKED = (1<< 0)
+};
+
+struct ClientSIPData
+{
+ void* owner;
+ SIPState state;
+ uint32_t flags;
+ char* userName;
+ char* clientUserAgent;
+ char* from;
+};
+
+struct SIP_CLIENT_APP_CONFIG
+{
+ int enabled;
+};
+
+// FIXIT-L THREAD_LOCAL?
+static SIP_CLIENT_APP_CONFIG sip_config;
+
+static CLIENT_APP_RETCODE sip_client_init(const IniClientAppAPI* 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,
+ AppIdData* flowp, Packet* pkt, Detector* userData,
+ const AppIdConfig* pConfig);
+static CLIENT_APP_RETCODE sip_tcp_client_init(const IniClientAppAPI* const init_api,
+ SF_LIST* config);
+static CLIENT_APP_RETCODE sip_tcp_client_validate(const uint8_t* data, uint16_t size, const int
+ dir,
+ AppIdData* flowp, Packet* pkt, Detector* userData,
+ const AppIdConfig* pConfig);
+static int sipAppGeClientApp(void* patternMatcher, char* pattern, uint32_t patternLen,
+ AppId* ClientAppId, char** clientVersion);
+static void sipUaClean(DetectorSipConfig* pConfig);
+static void sipServerClean(DetectorSipConfig* pConfig);
+
+RNAClientAppModule sip_udp_client_mod =
+{
+ "SIP",
+ IpProtocol::UDP,
+ &sip_client_init,
+ &sip_clean,
+ &sip_client_validate,
+ 2,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 1,
+ 0
+};
+RNAClientAppModule sip_tcp_client_mod =
+{
+ "SIP",
+ IpProtocol::TCP,
+ &sip_tcp_client_init,
+ nullptr,
+ &sip_tcp_client_validate,
+ 2,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 1,
+ 0
+};
+
+struct Client_App_Pattern
+{
+ const uint8_t* pattern;
+ unsigned length;
+ int index;
+ unsigned appId;
+};
+
+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 AppRegistryEntry appIdClientRegistry[] =
+{
+ { APP_ID_SIP, APPINFO_FLAG_CLIENT_ADDITIONAL|APPINFO_FLAG_CLIENT_USER },
+};
+
+static AppRegistryEntry appIdServiceRegistry[] =
+{
+ { APP_ID_SIP, APPINFO_FLAG_SERVICE_ADDITIONAL|APPINFO_FLAG_CLIENT_USER },
+ { APP_ID_RTP, APPINFO_FLAG_SERVICE_ADDITIONAL }
+};
+
+//service side
+struct ServiceSIPData
+{
+ uint8_t serverPkt;
+ char vendor[MAX_VENDOR_SIZE];
+};
+
+static int sip_service_init(const IniServiceAPI* const init_api);
+static int sip_service_validate(ServiceValidationArgs* args);
+
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &sip_service_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "sip"
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &sip_service_validate, SIP_PORT, IpProtocol::TCP, 0 },
+ { &sip_service_validate, SIP_PORT, IpProtocol::UDP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+SO_PUBLIC RNAServiceValidationModule sip_service_mod =
+{
+ svc_name,
+ &sip_service_init,
+ pp,
+ nullptr,
+ nullptr,
+ 1,
+ nullptr,
+ 0
+};
+
+static CLIENT_APP_RETCODE sip_client_init(const IniClientAppAPI* const init_api, SF_LIST*)
+{
+ 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++)
+ {
+ DebugFormat(DEBUG_LOG,"registering patterns: %s: %d\n",
+ (const char*)patterns[i].pattern, patterns[i].index);
+ init_api->RegisterPattern(&sip_client_validate, IpProtocol::UDP, patterns[i].pattern,
+ patterns[i].length, patterns[i].index, init_api->pAppidConfig);
+ }
+ }
+
+ unsigned j;
+ for (j=0; j < sizeof(appIdClientRegistry)/sizeof(*appIdClientRegistry); j++)
+ {
+ DebugFormat(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 IniClientAppAPI* const init_api,
+ SF_LIST* config)
+{
+ unsigned i;
+ RNAClientAppModuleConfigItem* item;
+
+ sip_config.enabled = 1;
+
+ if (config)
+ {
+ SF_LNODE* next;
+ for (item = (RNAClientAppModuleConfigItem*)sflist_first(config, &next); item;
+ item = (RNAClientAppModuleConfigItem*)sflist_next(&next))
+ {
+ DebugFormat(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++)
+ {
+ DebugFormat(DEBUG_LOG,"registering patterns: %s: %d\n",
+ (const char*)patterns[i].pattern, patterns[i].index);
+ init_api->RegisterPattern(&sip_tcp_client_validate, IpProtocol::TCP,
+ patterns[i].pattern, patterns[i].length,
+ patterns[i].index, init_api->pAppidConfig);
+ }
+ }
+
+ unsigned j;
+ for (j=0; j < sizeof(appIdClientRegistry)/sizeof(*appIdClientRegistry); j++)
+ {
+ DebugFormat(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 = (ClientSIPData*)data;
+ snort_free(fd->from);
+ snort_free(fd->clientUserAgent);
+ snort_free(fd->userName);
+ free (fd);
+}
+
+// static const char* const SIP_USRNAME_BEGIN_MARKER = "<sip:";
+static CLIENT_APP_RETCODE sip_client_validate(const uint8_t*, uint16_t, const int,
+ AppIdData* flowp, Packet*, struct Detector*, const AppIdConfig*)
+{
+ ClientSIPData* fd;
+
+ fd = (ClientSIPData*)sip_udp_client_mod.api->data_get(flowp,
+ sip_udp_client_mod.flow_data_index);
+ if (!fd)
+ {
+ fd = (ClientSIPData*)snort_calloc(sizeof(ClientSIPData));
+ sip_udp_client_mod.api->data_add(flowp, fd,
+ sip_udp_client_mod.flow_data_index, &clientDataFree);
+ 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,
+ AppIdData* flowp, Packet* pkt, struct Detector* userData,
+ const AppIdConfig* pConfig)
+{
+ return sip_client_validate(data, size, dir, flowp, pkt, userData, pConfig);
+}
+
+static int sipAppAddPattern(DetectorAppSipPattern** patternList, AppId ClientAppId,
+ const char* clientVersion, const char* serverPattern)
+{
+ /* Allocate memory for data structures */
+ DetectorAppSipPattern* pattern = (DetectorAppSipPattern*)snort_calloc(
+ sizeof(DetectorAppSipPattern));
+ pattern->userData.ClientAppId = ClientAppId;
+ pattern->userData.clientVersion = snort_strdup(clientVersion);
+ pattern->pattern.pattern = (uint8_t*)snort_strdup(serverPattern);
+ pattern->pattern.patternSize = (int)strlen(serverPattern);
+ pattern->next = *patternList;
+ *patternList = pattern;
+
+ return 0;
+}
+
+int sipUaPatternAdd(
+ AppId ClientAppId,
+ const char* clientVersion,
+ const char* pattern,
+ DetectorSipConfig* pSipConfig
+ )
+{
+ return sipAppAddPattern(&pSipConfig->appSipUaList, ClientAppId, clientVersion, pattern);
+}
+
+int sipServerPatternAdd(
+ AppId ClientAppId,
+ const char* clientVersion,
+ const char* pattern,
+ DetectorSipConfig* pSipConfig
+ )
+{
+ return sipAppAddPattern(&pSipConfig->appSipServerList, ClientAppId, clientVersion, pattern);
+}
+
+int sipUaFinalize(DetectorSipConfig* pSipConfig)
+{
+ const int PATTERN_PART_MAX=10;
+ static tMlmpPattern patterns[PATTERN_PART_MAX];
+ int num_patterns;
+ DetectorAppSipPattern* patternNode;
+
+ pSipConfig->sipUaMatcher = mlmpCreate();
+ if (!pSipConfig->sipUaMatcher)
+ return -1;
+
+ pSipConfig->sipServerMatcher = mlmpCreate();
+ if (!pSipConfig->sipServerMatcher)
+ {
+ mlmpDestroy((tMlmpTree*)pSipConfig->sipUaMatcher);
+ pSipConfig->sipUaMatcher = nullptr;
+ 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 = nullptr;
+
+ mlmpAddPattern((tMlmpTree*)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 = nullptr;
+
+ mlmpAddPattern((tMlmpTree*)pSipConfig->sipServerMatcher, patterns, patternNode);
+ }
+
+ mlmpProcessPatterns((tMlmpTree*)pSipConfig->sipUaMatcher);
+ mlmpProcessPatterns((tMlmpTree*)pSipConfig->sipServerMatcher);
+ return 0;
+}
+
+static void sipUaClean(DetectorSipConfig* pSipConfig)
+{
+ DetectorAppSipPattern* node;
+
+ if (pSipConfig->sipUaMatcher)
+ {
+ mlmpDestroy((tMlmpTree*)pSipConfig->sipUaMatcher);
+ pSipConfig->sipUaMatcher = nullptr;
+ }
+
+ for (node = pSipConfig->appSipUaList; node; node = pSipConfig->appSipUaList)
+ {
+ pSipConfig->appSipUaList = node->next;
+ snort_free((void*)node->pattern.pattern);
+ snort_free(node->userData.clientVersion);
+ snort_free(node);
+ }
+}
+
+static void sipServerClean(DetectorSipConfig* pSipConfig)
+{
+ DetectorAppSipPattern* node;
+
+ if (pSipConfig->sipServerMatcher)
+ {
+ mlmpDestroy((tMlmpTree*)pSipConfig->sipServerMatcher);
+ pSipConfig->sipServerMatcher = nullptr;
+ }
+
+ for (node = pSipConfig->appSipServerList; node; node = pSipConfig->appSipServerList)
+ {
+ pSipConfig->appSipServerList = node->next;
+ snort_free((void*)node->pattern.pattern);
+ snort_free(node->userData.clientVersion);
+ snort_free(node);
+ }
+}
+
+static int sipAppGeClientApp(
+ void* patternMatcher,
+ char* pattern,
+ uint32_t patternLen,
+ AppId* ClientAppId,
+ char** clientVersion)
+{
+ tMlmpPattern patterns[3];
+ DetectorAppSipPattern* data;
+
+ if (!pattern)
+ return 0;
+
+ patterns[0].pattern = (uint8_t*)pattern;
+ patterns[0].patternSize = patternLen;
+ patterns[1].pattern = nullptr;
+
+ data = (DetectorAppSipPattern*)mlmpMatchPatternGeneric((tMlmpTree*)patternMatcher, patterns);
+
+ if (data == nullptr)
+ return 0;
+
+ *ClientAppId = data->userData.ClientAppId;
+ *clientVersion = data->userData.clientVersion;
+
+ return 1;
+}
+
+static void createRtpFlow(AppIdData* flowp, const Packet* pkt, const sfip_t* cliIp, uint16_t
+ cliPort,
+ const sfip_t* srvIp, uint16_t srvPort, IpProtocol proto, int16_t app_id)
+{
+ AppIdData* 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_IGNORE_ID_FLAGS);
+ }
+
+ // 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_IGNORE_ID_FLAGS);
+ }
+}
+
+static int addFutureRtpFlows(AppIdData* flowp, const SipDialog* dialog, const Packet* p)
+{
+ SIP_MediaData* mdataA,* mdataB;
+
+ // check the first media session
+ if (nullptr == dialog->mediaSessions)
+ return -1;
+ // check the second media session
+ if (nullptr == dialog->mediaSessions->nextS)
+ return -1;
+
+ DebugFormat(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 ((nullptr != mdataA)&&(nullptr != mdataB))
+ {
+ DebugFormat(DEBUG_SIP, "Adding future channels Source IP: %s Port: %u\n",
+ sfip_to_str(&mdataA->maddress), mdataA->mport);
+ DebugFormat(DEBUG_SIP, "Adding future channels Destine IP: %s Port: %u\n",
+ sfip_to_str(&mdataB->maddress), mdataB->mport);
+
+ // FIXIT All of the casts in these two function calls are flagrantly wrong. These
+ // signatures don't line up and it doesn't seem to be a simple fix.
+ createRtpFlow(flowp, p, &mdataA->maddress, mdataA->mport, &mdataB->maddress,
+ mdataB->mport, IpProtocol::UDP, APP_ID_RTP);
+ createRtpFlow(flowp, p, &mdataB->maddress, mdataB->mport, &mdataA->maddress,
+ mdataA->mport, IpProtocol::UDP, APP_ID_RTP);
+
+ mdataA = mdataA->nextM;
+ mdataB = mdataB->nextM;
+ }
+ return 0;
+}
+
+static void SipSessionCbClientProcess(const Packet* p, const SipHeaders* headers, const
+ SipDialog* dialog, AppIdData* flowp)
+{
+ ClientSIPData* fd;
+ AppId ClientAppId = APP_ID_SIP;
+ char* clientVersion = nullptr;
+ int direction;
+
+ fd = (ClientSIPData*)sip_udp_client_mod.api->data_get(flowp,
+ sip_udp_client_mod.flow_data_index);
+ if (!fd)
+ {
+ fd = (ClientSIPData*)snort_calloc(sizeof(ClientSIPData));
+ sip_udp_client_mod.api->data_add(flowp, fd,
+ sip_udp_client_mod.flow_data_index, &clientDataFree);
+ 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 = (p->is_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)
+ {
+ snort_free(fd->from);
+ fd->from = strndup(headers->from, headers->fromLen);
+ }
+
+ if (headers->userName && headers->userNameLen)
+ {
+ snort_free(fd->userName);
+ fd->userName = strndup(headers->userName, headers->userNameLen);
+ }
+ if (headers->userAgent && headers->userAgentLen)
+ {
+ snort_free(fd->clientUserAgent);
+ fd->clientUserAgent = strndup(headers->userAgent, headers->userAgentLen);
+ }
+ }
+
+ if (fd->clientUserAgent)
+ {
+ if (sipAppGeClientApp(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 (sipAppGeClientApp(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(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(const Packet* p, const SipHeaders* headers, const
+ SipDialog* dialog, AppIdData* flowp)
+{
+ ServiceSIPData* ss;
+ int direction;
+
+ ss = (ServiceSIPData*)sip_service_mod.api->data_get(flowp, sip_service_mod.flow_data_index);
+ if (!ss)
+ {
+ ss = (ServiceSIPData*)snort_calloc(sizeof(ServiceSIPData));
+ sip_service_mod.api->data_add(flowp, ss, sip_service_mod.flow_data_index, &snort_free);
+ }
+
+ ss->serverPkt = 0;
+
+ direction = p->is_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 : nullptr, nullptr, nullptr);
+ }
+ }
+}
+
+void SipSessionSnortCallback(void*, ServiceEventType, void* data)
+{
+ AppIdData* flowp = nullptr;
+ SipEventData* eventData = (SipEventData*)data;
+
+ const Packet* p = eventData->packet;
+ const SipHeaders* headers = eventData->headers;
+ const SipDialog* dialog = eventData->dialog;
+
+#ifdef DEBUG_APP_ID_SESSIONS
+ {
+ char src_ip[INET6_ADDRSTRLEN];
+ char dst_ip[INET6_ADDRSTRLEN];
+ const sfip_t* ip;
+
+ src_ip[0] = 0;
+ ip = p->ptrs.ip_api.get_src();
+ inet_ntop(sfaddr_family(ip), (void*)sfaddr_get_ptr(ip), src_ip, sizeof(src_ip));
+ dst_ip[0] = 0;
+ ip = p->ptrs.ip_api.get_dst();
+ inet_ntop(sfaddr_family(ip), (void*)sfaddr_get_ptr(ip), dst_ip, sizeof(dst_ip));
+ fprintf(SF_DEBUG_FILE, "AppId Sip Snort Callback Session %s-%u -> %s-%u %d\n", src_ip,
+ (unsigned)p->src_port, dst_ip, (unsigned)p->dst_port, IsTCP(p) ? IpProtocol::TCP :
+ IpProtocol::UDP);
+ }
+#endif
+ if (p->flow)
+ flowp = getAppIdData(p->flow);
+
+ if (!flowp)
+ {
+ ErrorMessage("Missing session\n");
+ return;
+ }
+
+ SipSessionCbClientProcess(p, headers, dialog, flowp);
+ SipSessionCbServiceProcess(p, headers, dialog, flowp);
+}
+
+static int sip_service_init(const IniServiceAPI* const init_api)
+{
+ init_api->RegisterPattern(&sip_service_validate, IpProtocol::UDP, (const uint8_t*)SIP_BANNER,
+ SIP_BANNER_LEN, 0, svc_name, init_api->pAppidConfig);
+ init_api->RegisterPattern(&sip_service_validate, IpProtocol::TCP, (const uint8_t*)SIP_BANNER,
+ SIP_BANNER_LEN, 0, svc_name, init_api->pAppidConfig);
+ init_api->RegisterPattern(&sip_service_validate, IpProtocol::UDP, (const
+ uint8_t*)SIP_INVITE_BANNER, SIP_INVITE_BANNER_LEN, 0, svc_name, init_api->pAppidConfig);
+ init_api->RegisterPattern(&sip_service_validate, IpProtocol::TCP, (const
+ uint8_t*)SIP_INVITE_BANNER, SIP_INVITE_BANNER_LEN, 0, svc_name, init_api->pAppidConfig);
+ init_api->RegisterPattern(&sip_service_validate, IpProtocol::UDP, (const
+ uint8_t*)SIP_ACK_BANNER,
+ SIP_ACK_BANNER_LEN, 0, svc_name, init_api->pAppidConfig);
+ init_api->RegisterPattern(&sip_service_validate, IpProtocol::TCP, (const
+ uint8_t*)SIP_ACK_BANNER,
+ SIP_ACK_BANNER_LEN, 0, svc_name, init_api->pAppidConfig);
+ init_api->RegisterPattern(&sip_service_validate, IpProtocol::UDP, (const
+ uint8_t*)SIP_REGISTER_BANNER, SIP_REGISTER_BANNER_LEN, 0, svc_name,
+ init_api->pAppidConfig);
+ init_api->RegisterPattern(&sip_service_validate, IpProtocol::TCP, (const
+ uint8_t*)SIP_REGISTER_BANNER, SIP_REGISTER_BANNER_LEN, 0, svc_name,
+ init_api->pAppidConfig);
+ init_api->RegisterPattern(&sip_service_validate, IpProtocol::UDP, (const
+ uint8_t*)SIP_CANCEL_BANNER, SIP_CANCEL_BANNER_LEN, 0, svc_name, init_api->pAppidConfig);
+ init_api->RegisterPattern(&sip_service_validate, IpProtocol::TCP, (const
+ uint8_t*)SIP_CANCEL_BANNER, SIP_CANCEL_BANNER_LEN, 0, svc_name, init_api->pAppidConfig);
+ init_api->RegisterPattern(&sip_service_validate, IpProtocol::UDP, (const
+ uint8_t*)SIP_BYE_BANNER,
+ SIP_BYE_BANNER_LEN, 0, svc_name, init_api->pAppidConfig);
+ init_api->RegisterPattern(&sip_service_validate, IpProtocol::TCP, (const
+ uint8_t*)SIP_BYE_BANNER,
+ SIP_BYE_BANNER_LEN, 0, svc_name, init_api->pAppidConfig);
+ init_api->RegisterPattern(&sip_service_validate, IpProtocol::UDP, (const
+ uint8_t*)SIP_OPTIONS_BANNER, SIP_OPTIONS_BANNER_LEN, 0, svc_name, init_api->pAppidConfig);
+ init_api->RegisterPattern(&sip_service_validate, IpProtocol::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++)
+ {
+ DebugFormat(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;
+ AppIdData* flowp = args->flowp;
+
+ ss = (ServiceSIPData*)sip_service_mod.api->data_get(flowp, sip_service_mod.flow_data_index);
+ if (!ss)
+ {
+ ss = (ServiceSIPData*)snort_calloc(sizeof(ServiceSIPData));
+ sip_service_mod.api->data_add(flowp, ss, sip_service_mod.flow_data_index, &snort_free);
+ }
+
+ 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);
+ }
+ 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);
+ }
+
+ return SERVICE_INPROCESS;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// detector_sip.h author Sourcefire Inc.
+
+#ifndef DETECTOR_SIP_H
+#define DETECTOR_SIP_H
+
+// AppId structures for SIP detection
+
+#include "detector_api.h"
+#include "util/sf_multi_mpse.h"
+
+struct RNAServiceValidationModule;
+
+struct SipUaUserData
+{
+ AppId ClientAppId;
+ char* clientVersion;
+};
+
+struct DetectorAppSipPattern
+{
+ tMlpPattern pattern;
+ SipUaUserData userData;
+ DetectorAppSipPattern* next;
+};
+
+struct DetectorSipConfig
+{
+ void* sipUaMatcher;
+ DetectorAppSipPattern* appSipUaList;
+ void* sipServerMatcher;
+ DetectorAppSipPattern* appSipServerList;
+};
+
+extern struct RNAClientAppModule sip_udp_client_mod;
+extern struct RNAClientAppModule sip_tcp_client_mod;
+extern struct RNAServiceValidationModule sip_service_mod;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// http_url_patterns.cc author Sourcefire Inc.
+
+#include "http_url_patterns.h"
+
+#include <string.h>
+
+#include "log/messages.h"
+
+#include "application_ids.h"
+#include "http_common.h"
+#include "util/sf_multi_mpse.h"
+#include "util/sf_mlmp.h"
+#include "utils/util.h"
+
+static const char* const FP_OPERATION_AND = "%&%";
+static const unsigned PATTERN_PART_MAX = 10;
+
+static void destroyHosUrlDetectorPattern(HosUrlDetectorPattern* pattern)
+{
+ if (!pattern)
+ return;
+
+ destroyHosUrlDetectorPattern(pattern->next);
+
+ if (pattern->host.pattern)
+ snort_free(*(void**)&pattern->host.pattern);
+ if (pattern->path.pattern)
+ snort_free(*(void**)&pattern->path.pattern);
+ if (pattern->query.pattern)
+ snort_free(*(void**)&pattern->query.pattern);
+ snort_free(pattern);
+}
+
+static int addHosUrlPatternToList(HosUrlDetectorPattern* detector,
+ HosUrlPatternsList** hosUrlPatternsList)
+{
+ if (!detector)
+ return -1;
+
+ if (!(*hosUrlPatternsList))
+ {
+ *hosUrlPatternsList = (HosUrlPatternsList*)snort_calloc(sizeof(HosUrlPatternsList));
+ (*hosUrlPatternsList)->head = detector;
+ (*hosUrlPatternsList)->tail = detector;
+ }
+ else
+ {
+ (*hosUrlPatternsList)->tail->next = detector;
+ (*hosUrlPatternsList)->tail = detector;
+ }
+
+ return 0;
+}
+
+void destroyHosUrlPatternList(HosUrlPatternsList** pHosUrlPatternsList)
+{
+ if (!(*pHosUrlPatternsList))
+ return;
+
+ destroyHosUrlDetectorPattern((*pHosUrlPatternsList)->head);
+ snort_free(*pHosUrlPatternsList);
+ *pHosUrlPatternsList = nullptr;
+}
+
+int addMlmpPattern(void* hosUrlMatcher, HosUrlPatternsList** hosUrlPatternsList,
+ 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,
+ AppId 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 (!hosUrlMatcher)
+ return -1;
+
+ HosUrlDetectorPattern* detector = (HosUrlDetectorPattern*)snort_calloc(
+ sizeof(HosUrlDetectorPattern));
+ detector->host.pattern = (uint8_t*)snort_strdup((char*)host_pattern);
+
+ if (path_pattern)
+ detector->path.pattern = (uint8_t*)snort_strdup((char*)path_pattern);
+ else
+ detector->path.pattern = nullptr;
+
+ if (query_pattern)
+ detector->query.pattern = (uint8_t*)snort_strdup((char*)query_pattern);
+ else
+ detector->query.pattern = nullptr;
+
+ 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 = nullptr;
+ 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 = nullptr;
+
+ if (addHosUrlPatternToList(detector, hosUrlPatternsList))
+ return -1;
+
+ return mlmpAddPattern((tMlmpTree*)hosUrlMatcher, patterns, detector);
+}
+
+uint32_t parseMultipleHTTPPatterns(const char* pattern, tMlmpPattern* parts, uint32_t
+ numPartLimit, int level)
+{
+ uint32_t partNum = 0;
+ const char* tmp;
+ uint32_t i;
+
+ if (!pattern)
+ return 0;
+
+ tmp = pattern;
+ while (tmp && (partNum < numPartLimit))
+ {
+ const char* 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*)snort_strdup(tmp);
+ parts[partNum].patternSize = strlen((const char*)parts[partNum].pattern);
+ tmp = nullptr;
+ }
+ parts[partNum].level = level;
+
+ if (!parts[partNum].pattern)
+ {
+ for (i = 0; i <= partNum; i++)
+ snort_free((void*)parts[i].pattern);
+
+ ErrorMessage("Failed to allocate memory");
+ return 0;
+ }
+ partNum++;
+ }
+
+ return partNum;
+}
+
+/**recursively destroy matcher.
+ */
+void destroyHosUrlMatcher(tMlmpTree** hosUrlMatcher)
+{
+ if (hosUrlMatcher && *hosUrlMatcher)
+ {
+ mlmpDestroy(*hosUrlMatcher);
+ *hosUrlMatcher = nullptr;
+ }
+}
+
+int matchQueryElements(
+ tMlpPattern* packetData,
+ tMlpPattern* userPattern,
+ char* appVersion,
+ size_t appVersionSize
+ )
+{
+ const uint8_t* index, * endKey;
+ const uint8_t* queryEnd;
+ uint32_t extractedSize;
+ uint32_t copySize = 0;
+
+ if (appVersion == nullptr)
+ 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 = (const uint8_t*)memchr (index, '&', queryEnd - index);
+ if (!endKey)
+ endKey = queryEnd;
+
+ if (userPattern->patternSize < (uint32_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;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// http_url_patterns.h author Sourcefire Inc.
+
+#ifndef HTTP_URL_PATTERNS_H
+#define HTTP_URL_PATTERNS_H
+
+// List of URL pattern match functions
+
+#include "http_common.h"
+
+struct tMlmpPattern;
+
+int addMlmpPattern(void* mlpMatcher, HosUrlPatternsList** hosUrlPatternsList, 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,
+ AppId appId, uint32_t payload_id, uint32_t service_id, uint32_t client_id, DHPSequence seq);
+void destroyHosUrlMatcher(tMlmpTree** 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 destroyHosUrlPatternList(HosUrlPatternsList** pHosUrlPatternsList);
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// dns_defs.h author Sourcefire Inc.
+
+#ifndef DNS_DEFS_H
+#define DNS_DEFS_H
+
+#include <cstdint>
+
+#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
+
+#pragma pack(1)
+
+// FIXIT-H J bitfields should be replaced with getters/setters
+struct DNSHeader
+{
+ 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;
+};
+
+struct DNSTCPHeader
+{
+ uint16_t length;
+};
+
+struct DNSLabel
+{
+ uint8_t len;
+ uint8_t name;
+};
+
+struct DNSLabelPtr
+{
+ uint16_t position;
+ uint8_t data;
+};
+
+struct DNSLabelBitfield
+{
+ uint8_t id;
+ uint8_t len;
+ uint8_t data;
+};
+
+struct DNSQueryFixed
+{
+ uint16_t QType;
+ uint16_t QClass;
+};
+
+struct DNSAnswerData
+{
+ uint16_t type;
+ uint16_t klass;
+ uint32_t ttl;
+ uint16_t r_len;
+};
+
+#pragma pack()
+
+#endif
+
--- /dev/null
+#!/bin/bash
+
+# run make on selected files from MANIFEST.txt
+
+USAGE="$0 [-h] [<status>] [<cisco_username>]"
+
+if [[ $1 == -h ]]; then
+ echo $USAGE >&2
+ exit
+fi
+
+file_status=$1
+cisco_username=$2
+filter=
+
+if [[ -n "$file_status" ]]; then
+ filter="$filter *$file_status"
+fi
+
+if [[ -n "$cisco_username" ]]; then
+ filter="$filter *$USER"
+fi
+
+grep -v ^# MANIFEST.txt | while IFS='' read -r line || [[ -n "$line" ]]; do
+ echo $line | grep "$filter" >/dev/null && {
+ filename=$(echo $line | awk '{print $1}')
+ make "${filename%.*}_${filename##*.}" >/dev/null || echo $filename
+ }
+done
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// flow_error.h author Sourcefire Inc.
+
+#ifndef FLOW_ERROR_H
+#define FLOW_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
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// fw_appid.cc author Sourcefire Inc.
+
+#include "fw_appid.h"
+
+#include "log/messages.h"
+#include "profiler/profiler.h"
+#include "protocols/tcp.h"
+#include "target_based/snort_protocols.h"
+#include "utils/util.h"
+
+#include "appid_stats.h"
+#include "app_forecast.h"
+#include "app_info_table.h"
+
+#include "host_port_app_cache.h"
+#include "lua_detector_module.h"
+#include "client_plugins/client_app_base.h"
+#include "detector_plugins/detector_http.h"
+#include "detector_plugins/detector_dns.h"
+#include "service_plugins/service_base.h"
+#include "service_plugins/service_ssl.h"
+#include "service_plugins/service_util.h"
+#include "util/common_util.h"
+#include "util/ip_funcs.h"
+#include "util/network_set.h"
+#include "time/packet_time.h"
+#include "sfip/sf_ip.h"
+#include "stream/stream_api.h"
+
+#if 1 // FIXIT-M hacks
+struct HttpParsedHeaders
+{
+ struct HttpBuf
+ {
+ char* start;
+ int len;
+ };
+
+ HttpBuf host;
+ HttpBuf url;
+ HttpBuf userAgent;
+ HttpBuf referer;
+ HttpBuf via;
+ HttpBuf contentType;
+ HttpBuf responseCode;
+};
+
+#define HTTP_XFF_FIELD_X_FORWARDED_FOR ""
+#define HTTP_XFF_FIELD_TRUE_CLIENT_IP ""
+
+#endif
+
+#define MAX_ATTR_LEN 1024
+#define HTTP_PREFIX "http://"
+
+#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;
+
+ProfileStats tpPerfStats;
+ProfileStats tpLibPerfStats;
+ProfileStats httpPerfStats;
+ProfileStats clientMatchPerfStats;
+ProfileStats serviceMatchPerfStats;
+
+#define HTTP_PATTERN_MAX_LEN 1024
+#define PORT_MAX 65535
+
+unsigned long app_id_raw_packet_count = 0;
+unsigned long app_id_processed_packet_count = 0;
+unsigned long app_id_ignored_packet_count = 0;
+int app_id_debug;
+static int ptype_scan_counts[NUMBER_OF_PTYPES];
+
+static void ProcessThirdPartyResults(Packet* p, AppIdData* appIdSession, int confidence,
+ AppId* proto_list, ThirdPartyAppIDAttributeData* attribute_data);
+static void ExamineRtmpMetadata(AppIdData* appIdSession);
+
+void dump_appid_stats()
+{
+ LogMessage("Application Identification Preprocessor:\n");
+ LogMessage(" Total packets received : %lu\n", app_id_raw_packet_count);
+ LogMessage(" Total packets processed : %lu\n", app_id_processed_packet_count);
+ if (thirdparty_appid_module)
+ thirdparty_appid_module->print_stats();
+ LogMessage(" Total packets ignored : %lu\n", app_id_ignored_packet_count);
+ AppIdServiceStateDumpStats();
+ RNAPndDumpLuaStats();
+}
+
+void reset_appid_stats(int, void*)
+{
+ app_id_raw_packet_count = 0;
+ app_id_processed_packet_count = 0;
+ app_id_ignored_packet_count = 0;
+ if (thirdparty_appid_module)
+ thirdparty_appid_module->reset_stats();
+}
+
+
+/* The UNSYNCED_SNORT_ID value is to cheaply insure we get
+ the value from snort rather than assume */
+#define UNSYNCED_SNORT_ID 0x5555
+
+AppIdData* appSharedDataAlloc(IpProtocol proto, const sfip_t* ip)
+{
+ // FIXIT - should appid_flow_data_id be global to all threads?
+ static THREAD_LOCAL uint32_t appid_flow_data_id = 0;
+ AppIdData* data = new AppIdData;
+
+ // should be freed by flow
+ // if (app_id_free_list)
+ // {
+ // data = app_id_free_list;
+ // app_id_free_list = data->next;
+ // data->reset();
+ // }
+
+ if ( !data )
+ FatalError("Could not allocate AppIdData data");
+
+ if (thirdparty_appid_module)
+ if (!(data->tpsession = thirdparty_appid_module->session_create()))
+ FatalError("Could not allocate AppIdData->tpsession data");
+
+ data->id = ++appid_flow_data_id;
+ data->common.fsf_type.flow_type = APPID_SESSION_TYPE_NORMAL;
+ data->proto = proto;
+ data->common.initiator_ip = *ip;
+ data->snortId = UNSYNCED_SNORT_ID;
+ data->search_support_type = SEARCH_SUPPORT_TYPE_UNKNOWN;
+ return data;
+}
+
+static inline AppIdData* appSharedCreateData(const Packet* p, IpProtocol proto, int direction)
+{
+ AppIdData* data;
+ const sfip_t* ip;
+
+ ip = (direction == APP_ID_FROM_INITIATOR)
+ ? p->ptrs.ip_api.get_src() : p->ptrs.ip_api.get_dst();
+ data = appSharedDataAlloc(proto, ip);
+
+ if ( ( proto == IpProtocol::TCP || proto == IpProtocol::UDP ) && ( p->ptrs.sp != p->ptrs.dp ) )
+ data->common.initiator_port =
+ (direction == APP_ID_FROM_INITIATOR) ? p->ptrs.sp : p->ptrs.dp;
+ data->ssn = p->flow;
+ data->stats.firstPktsecond = p->pkth->ts.tv_sec;
+
+ p->flow->set_application_data(data);
+ return data;
+}
+
+static inline void appSharedReInitData(AppIdData* session)
+{
+ session->miscAppId = APP_ID_NONE;
+
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ //data
+ if (isSslServiceAppId(session->tpAppId))
+ {
+ session->payloadAppId = session->referredPayloadAppId = session->tpPayloadAppId =
+ APP_ID_NONE;
+ clearAppIdFlag(session, APPID_SESSION_CONTINUE);
+ if (session->payloadVersion)
+ {
+ snort_free(session->payloadVersion);
+ session->payloadVersion = nullptr;
+ }
+ if (session->hsession && session->hsession->url)
+ {
+ snort_free(session->hsession->url);
+ session->hsession->url = nullptr;
+ }
+ }
+#endif
+
+ //service
+ if (!getAppIdFlag(session, APPID_SESSION_STICKY_SERVICE))
+ {
+ clearAppIdFlag(session, APPID_SESSION_STICKY_SERVICE);
+
+ session->tpAppId = session->serviceAppId = session->portServiceAppId = APP_ID_NONE;
+ if (session->serviceVendor)
+ {
+ snort_free(session->serviceVendor);
+ session->serviceVendor = nullptr;
+ }
+ if (session->serviceVersion)
+ {
+ snort_free(session->serviceVersion);
+ session->serviceVersion = nullptr;
+ }
+
+ session->service_ip.clear();
+ session->service_port = 0;
+ session->rnaServiceState = RNA_STATE_NONE;
+ session->serviceData = nullptr;
+ AppIdFlowdataDeleteAllByMask(session, APPID_SESSION_DATA_SERVICE_MODSTATE_BIT);
+ }
+
+ //client
+ session->ClientAppId = session->ClientServiceAppId = APP_ID_NONE;
+ if (session->clientVersion)
+ {
+ snort_free(session->clientVersion);
+ session->clientVersion = nullptr;
+ }
+ session->rnaClientState = RNA_STATE_NONE;
+ 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(AppIdConfig* pConfig)
+{
+#ifdef RNA_FULL_CLEANUP
+ AppIdData* app_id;
+ TmpAppIdData* tmp_app_id;
+
+ while ((app_id = app_id_free_list))
+ {
+ app_id_free_list = app_id->next;
+ snort_free(app_id);
+ }
+
+ while ((tmp_app_id = tmp_app_id_free_list))
+ {
+ tmp_app_id_free_list = tmp_app_id->next;
+ snort_free(tmp_app_id);
+ }
+ AppIdFlowdataFini();
+#endif
+
+ appInfoTableFini(pConfig);
+}
+
+static inline int PENetworkMatch(const sfip_t* pktAddr, const PortExclusion* pe)
+{
+ const uint32_t* pkt = pktAddr->ip32;
+ const uint32_t* nm = pe->netmask.u6_addr32;
+ const uint32_t* peIP = pe->ip.u6_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 Packet* pkt, int reversed)
+{
+ SF_LIST** src_port_exclusions;
+ SF_LIST** dst_port_exclusions;
+ SF_LIST* pe_list;
+ PortExclusion* pe;
+ const sfip_t* s_ip;
+ uint16_t port;
+ AppIdConfig* pConfig = pAppidActiveConfig;
+
+ if ( pkt->is_tcp() )
+ {
+ src_port_exclusions = pConfig->tcp_port_exclusions_src;
+ dst_port_exclusions = pConfig->tcp_port_exclusions_dst;
+ }
+ else if ( pkt->is_udp() )
+ {
+ 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->ptrs.dp : pkt->ptrs.sp;
+ if ( port && (pe_list=src_port_exclusions[port]) != nullptr )
+ {
+ s_ip = reversed ? pkt->ptrs.ip_api.get_dst() : pkt->ptrs.ip_api.get_src();
+
+ SF_LNODE* node;
+
+ /* walk through the list of port exclusions for this port */
+ for ( pe = (PortExclusion*)sflist_first(pe_list, &node);
+ pe;
+ pe = (PortExclusion*)sflist_next(&node) )
+ {
+ 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->ptrs.sp : pkt->ptrs.dp;
+ if ( port && (pe_list=dst_port_exclusions[port]) != nullptr )
+ {
+ s_ip = reversed ? pkt->ptrs.ip_api.get_src() : pkt->ptrs.ip_api.get_dst();
+
+ SF_LNODE* node;
+ /* walk through the list of port exclusions for this port */
+ for ( pe = (PortExclusion*)sflist_first(pe_list, &node);
+ pe;
+ pe = (PortExclusion*)sflist_next(&node) )
+ {
+ 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(Flow* flow, AppIdData* session, volatile int debug_flag,
+ FWDebugSessionConstraints* info, char* debug_session, int direction)
+{
+ if (debug_flag)
+ {
+ assert(flow);
+ const FlowKey* key = flow->key;
+
+ if (((info->protocol == PktType::NONE) || info->protocol == key->pkt_type) &&
+ (((!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;
+ sfip_t sip;
+ sfip_t dip;
+ unsigned offset;
+ uint16_t sport;
+ uint16_t dport;
+ char sipstr[INET6_ADDRSTRLEN];
+ char dipstr[INET6_ADDRSTRLEN];
+ 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)
+ {
+ sfip_set_raw(&sip, key->ip_l, AF_INET);
+ sfip_set_raw(&dip, key->ip_l, AF_INET);
+ sport = key->port_l;
+ dport = key->port_h;
+ }
+ else
+ {
+ sfip_set_raw(&sip, key->ip_l, AF_INET);
+ sfip_set_raw(&dip, key->ip_l, AF_INET);
+ sport = key->port_h;
+ dport = key->port_l;
+ }
+ }
+ // FIXIT - sfip_fast_eq6 is stub macro
+ else if (sfip_fast_eq6(&session->common.initiator_ip, key->ip_l) == 0)
+ {
+ sfip_set_raw(&sip, key->ip_l, AF_INET);
+ sfip_set_raw(&dip, key->ip_h, AF_INET);
+ sport = key->port_l;
+ dport = key->port_h;
+ }
+ else
+ {
+ sfip_set_raw(&sip, key->ip_l, AF_INET);
+ sfip_set_raw(&dip, key->ip_h, AF_INET);
+ sport = key->port_h;
+ dport = key->port_l;
+ }
+ }
+ else
+ {
+ sfip_set_raw(&sip, key->ip_l, AF_INET);
+ sfip_set_raw(&dip, key->ip_h, AF_INET);
+ sport = key->port_l;
+ dport = key->port_h;
+ }
+ sipstr[0] = 0;
+ if ( sip.ip32[0] || sip.ip32[1] || sip.ip16[4] ||
+ (sip.ip16[5] && sip.ip16[5] != 0xffff) )
+ {
+ af = AF_INET6;
+ offset = 0;
+ }
+ else
+ {
+ af = AF_INET;
+ offset = 12;
+ }
+
+ inet_ntop(af, &sip.ip8[offset], sipstr, sizeof(sipstr));
+ dipstr[0] = 0;
+ if ( dip.ip32[0] || dip.ip32[1] || dip.ip16[4] ||
+ (dip.ip16[5] && dip.ip16[5] != 0xFFFF) )
+ {
+ af = AF_INET6;
+ offset = 0;
+ }
+ else
+ {
+ af = AF_INET;
+ offset = 12;
+ }
+
+ inet_ntop(af, &dip.ip8[offset], dipstr, sizeof(dipstr));
+#ifdef HAVE_DAQ_ADDRESS_SPACE_ID
+ 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->pkt_type,
+ (direction == APP_ID_FROM_INITIATOR) ? "" : " R",
+ (unsigned)key->addressSpaceId, get_instance_id());
+#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->pkt_type,
+ (direction == APP_ID_FROM_INITIATOR) ? "" : " R", get_instance_id());
+#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 = static_cast<PktType>(*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.u6_addr32[1] || info->sip.u6_addr32[2] || info->sip.u6_addr32[3])
+ info->sip_flag = 1;
+ else if (info->sip.u6_addr32[0])
+ {
+ info->sip.u6_addr32[3] = info->sip.u6_addr32[0];
+ info->sip.u6_addr32[0] = 0;
+ info->sip.u6_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.u6_addr32[1] || info->dip.u6_addr32[2] || info->dip.u6_addr32[3])
+ info->dip_flag = 1;
+ else if (info->dip.u6_addr32[0])
+ {
+ info->dip.u6_addr32[3] = info->dip.u6_addr32[0];
+ info->dip.u6_addr32[0] = 0;
+ info->dip.u6_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 != PktType::NONE || 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.u6_addr32[0] && !info->sip.u6_addr32[0] && !info->sip.u6_addr16[4] &&
+ info->sip.u6_addr16[5] == 0xFFFF)
+ {
+ saf = AF_INET;
+ }
+ else
+ saf = AF_INET6;
+ if (!info->dip.u6_addr32[0] && !info->dip.u6_addr32[0] && !info->dip.u6_addr16[4] &&
+ info->dip.u6_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.u6_addr32[3] : info->sip.u6_addr32, sipstr,
+ sizeof(sipstr));
+ dipstr[0] = 0;
+ inet_ntop(daf, daf == AF_INET ? &info->dip.u6_addr32[3] : info->dip.u6_addr32, dipstr,
+ sizeof(dipstr));
+ LogMessage("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
+ LogMessage("Debugging %s disabled\n", desc);
+}
+
+int AppIdDebug(uint16_t, const uint8_t* const data, uint32_t length, void**, char*, int)
+{
+ 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;
+ AppIdConfig* pConfig = pAppidActiveConfig;
+
+ 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 Packet* p, int dst)
+{
+ const sfip_t* sf_ip;
+ NetworkSet* net_list;
+ unsigned flags;
+ int32_t zone;
+ NSIPv6Addr ip6;
+ AppIdConfig* pConfig = pAppidActiveConfig;
+
+ if (!dst)
+ {
+ zone = p->pkth->ingress_group;
+ sf_ip = p->ptrs.ip_api.get_src();
+ }
+ else
+ {
+ zone = (p->pkth->egress_index == DAQ_PKTHDR_UNKNOWN) ? p->pkth->ingress_group
+ :
+ p->pkth->egress_group;
+ if (zone == DAQ_PKTHDR_FLOOD)
+ return 0;
+ sf_ip = p->ptrs.ip_api.get_dst();
+ }
+
+ 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 ( sf_ip->is_ip4() )
+ {
+ if (sf_ip->ip32[0] == 0xFFFFFFFF)
+ return IPFUNCS_CHECKED;
+ NetworkSet_ContainsEx(net_list, ntohl(sf_ip->ip32[0]), &flags);
+ }
+ else
+ {
+ // FIXIT - the sfaddr_get_ptr macro is NULL, not good for memcpy
+ // memcpy(&ip6, sfaddr_get_ptr(sf_ip), sizeof(ip6));
+ NSIPv6AddrNtoH(&ip6);
+ NetworkSet_Contains6Ex(net_list, &ip6, &flags);
+ }
+
+ return flags | IPFUNCS_CHECKED;
+}
+
+static inline bool isSpecialSessionMonitored(const Packet* p)
+{
+ if ( p->is_ip4() )
+ {
+ if (p->is_udp() && ((p->ptrs.sp == 68 && p->ptrs.dp == 67)
+ || (p->ptrs.sp == 67 && p->ptrs.dp == 68)))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+static inline uint64_t isSessionMonitored(const Packet* p, int dir, AppIdData* session)
+{
+ uint64_t flags;
+// FIXIT-M: Determine correct replacement for this _dpd call
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ uint64_t flow_flags = _dpd.isAppIdRequired() ? APPID_SESSION_DISCOVER_APP : 0;
+#else
+ uint64_t flow_flags = APPID_SESSION_DISCOVER_APP;
+#endif
+
+ 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;
+ 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;
+ 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;
+ 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;
+ 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;
+ 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;
+ 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;
+}
+
+static inline void seServiceAppIdData(AppIdData* session, AppId 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;
+
+ if (pAppidActiveConfig->mod_config->instance_id)
+ checkSandboxDetection(serviceAppId);
+
+ /* Clear out previous values of vendor & version */
+ if (session->serviceVendor)
+ {
+ snort_free(session->serviceVendor);
+ session->serviceVendor = nullptr;
+ }
+ if (session->serviceVersion)
+ {
+ snort_free(session->serviceVersion);
+ session->serviceVersion = nullptr;
+ }
+
+ if (vendor)
+ session->serviceVendor = vendor;
+
+ if (version && *version)
+ {
+ session->serviceVersion = *version;
+ *version = nullptr;
+ }
+ }
+ else
+ {
+ if (vendor || version)
+ {
+ /* Clear previous values */
+ if (session->serviceVendor)
+ snort_free(session->serviceVendor);
+ if (session->serviceVersion)
+ snort_free(session->serviceVersion);
+
+ /* set vendor */
+ if (vendor)
+ session->serviceVendor = vendor;
+ else
+ session->serviceVendor = nullptr;
+
+ /* set version */
+ if (version && *version)
+ {
+ session->serviceVersion = *version;
+ *version = nullptr;
+ }
+ else
+ session->serviceVersion = nullptr;
+ }
+ }
+}
+
+static inline void setClientAppIdData(AppIdData* session, AppId ClientAppId, char** version)
+{
+ AppIdConfig* pConfig = pAppidActiveConfig;
+ if (ClientAppId <= APP_ID_NONE || ClientAppId == APP_ID_HTTP)
+ return;
+
+ if (session->ClientAppId != ClientAppId)
+ {
+ unsigned prev_priority = appInfoEntryPriorityGet(session->ClientAppId, pConfig);
+ unsigned curr_priority = appInfoEntryPriorityGet(ClientAppId, pConfig);
+
+ if (pAppidActiveConfig->mod_config->instance_id)
+ checkSandboxDetection(ClientAppId);
+
+ if ((session->ClientAppId) && (prev_priority > curr_priority ))
+ return;
+ session->ClientAppId = ClientAppId;
+
+ if (session->clientVersion)
+ snort_free(session->clientVersion);
+
+ if (version && *version)
+ {
+ session->clientVersion = *version;
+ *version = nullptr;
+ }
+ else
+ session->clientVersion = nullptr;
+ }
+ else if (version && *version)
+ {
+ if (session->clientVersion)
+ snort_free(session->clientVersion);
+ session->clientVersion = *version;
+ *version = nullptr;
+ }
+}
+
+static inline void setReferredPayloadAppIdData(AppIdData* session, AppId referredPayloadAppId)
+{
+ if (referredPayloadAppId <= APP_ID_NONE)
+ return;
+
+ if (session->referredPayloadAppId != referredPayloadAppId)
+ {
+ if (pAppidActiveConfig->mod_config->instance_id)
+ checkSandboxDetection(referredPayloadAppId);
+
+ session->referredPayloadAppId = referredPayloadAppId;
+ }
+}
+
+static inline void setPayloadAppIdData(AppIdData* session, ApplicationId payloadAppId,
+ char** version)
+{
+ AppIdConfig* pConfig = pAppidActiveConfig;
+
+ if (payloadAppId <= APP_ID_NONE)
+ return;
+
+ if (session->payloadAppId != payloadAppId)
+ {
+ unsigned prev_priority = appInfoEntryPriorityGet(session->payloadAppId, pConfig);
+ unsigned curr_priority = appInfoEntryPriorityGet(payloadAppId, pConfig);
+
+ if (pAppidActiveConfig->mod_config->instance_id)
+ checkSandboxDetection(payloadAppId);
+
+ if ((session->payloadAppId ) && (prev_priority > curr_priority ))
+ return;
+
+ session->payloadAppId = payloadAppId;
+
+ if (session->payloadVersion)
+ snort_free(session->payloadVersion);
+
+ if (version && *version)
+ {
+ session->payloadVersion = *version;
+ *version = nullptr;
+ }
+ else
+ session->payloadVersion = nullptr;
+ }
+ else if (version && *version)
+ {
+ if (session->payloadVersion)
+ snort_free(session->payloadVersion);
+ session->payloadVersion = *version;
+ *version = nullptr;
+ }
+}
+
+static inline void clearSessionAppIdData(AppIdData* session)
+{
+ session->payloadAppId = APP_ID_UNKNOWN;
+ session->serviceAppId = APP_ID_UNKNOWN;
+ session->tpPayloadAppId = APP_ID_UNKNOWN;
+ session->tpAppId = APP_ID_UNKNOWN;
+ if (session->payloadVersion)
+ {
+ snort_free(session->payloadVersion);
+ session->payloadVersion = nullptr;
+ }
+ if (session->serviceVendor)
+ {
+ snort_free(session->serviceVendor);
+ session->serviceVendor = nullptr;
+ }
+ if (session->serviceVersion)
+ {
+ snort_free(session->serviceVersion);
+ session->serviceVersion = nullptr;
+ }
+
+ if (session->tsession)
+ session->appTlsSessionDataFree();
+
+ if (session->hsession)
+ session->appHttpSessionDataFree();
+
+ if (session->dsession)
+ session->appDNSSessionDataFree();
+
+ if (thirdparty_appid_module)
+ thirdparty_appid_module->session_delete(session->tpsession, 1);
+}
+
+static inline int initial_CHP_sweep(char** chp_buffers, MatchedCHPAction** ppmatches,
+ AppIdData* session, const DetectorHttpConfig* pHttpConfig)
+{
+ CHPApp* cah = nullptr;
+ int longest = 0;
+ int size, i;
+ httpSession* hsession;
+ int scanKeyFoundSomething=0;
+ CHPMatchTally* pTally = nullptr; // scanKeyCHP allocates a pointer, but we free it when ready
+
+ hsession = session->hsession;
+
+ for (i = 0; i <= MAX_KEY_PATTERN; i++)
+ {
+ ppmatches[i] = nullptr;
+ if (chp_buffers[i] && (size = strlen(chp_buffers[i])) &&
+ scanKeyCHP((PatternType)i, chp_buffers[i], size, &pTally, &ppmatches[i], pHttpConfig))
+ scanKeyFoundSomething=1;
+ }
+ if (!scanKeyFoundSomething)
+ 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 == nullptr)
+ {
+ // 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] = nullptr;
+ }
+ }
+ 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] +
+ cah->ptype_rewrite_insert_used[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 const 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(AppIdData* session, char** version, Packet* p, const
+ AppIdConfig* pConfig)
+{
+ int i, size;
+ int found_in_buffer = 0;
+ char* user = nullptr;
+ AppId chp_final;
+ AppId 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,
+ };
+
+ char* chp_rewritten[NUMBER_OF_PTYPES] =
+ {
+ nullptr,nullptr,nullptr,
+ nullptr,nullptr,nullptr,
+ nullptr,nullptr,nullptr
+ };
+
+ MatchedCHPAction* chp_matches[NUMBER_OF_PTYPES] =
+ {
+ nullptr,nullptr,nullptr,
+ nullptr,nullptr,nullptr,
+ nullptr,nullptr,nullptr
+ };
+
+ 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.
+ for (i = 0; i < NUMBER_OF_PTYPES; i++)
+ {
+ if (http_session->new_field[i])
+ {
+ snort_free(http_session->new_field[i]);
+ http_session->new_field[i] = nullptr;
+ }
+ }
+
+ if (!initial_CHP_sweep(chp_buffers, 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] && chp_buffers[i] && (size = strlen(chp_buffers[i])) > 0)
+ {
+ found_in_buffer = 0;
+ ret = scanCHP((PatternType)i, chp_buffers[i], size, chp_matches[i], version,
+ &user, &chp_rewritten[i], &found_in_buffer,
+ http_session, p, &pConfig->detectorHttpConfig);
+ chp_matches[i] = nullptr; // freed by scanCHP()
+ http_session->total_found += found_in_buffer;
+ http_session->num_scans--;
+ ptype_scan_counts[i] = 0;
+ // Give up if scanCHP returns nothing, OR
+ // (if we did not match the right numbher of patterns in this field AND EITHER
+ // (there is no match quota [all must match]) OR
+ // (the total number of matches is less than our match quota))
+ if (!ret ||
+ (found_in_buffer < http_session->ptype_req_counts[i] &&
+ (!http_session->num_matches ||
+ http_session->total_found < http_session->num_matches)))
+ {
+ http_session->chp_candidate = 0;
+ break;
+ }
+ /* We are finished if we have a num_matches target and we've met it or
+ if we have done all the scans */
+ if (!http_session->num_scans ||
+ (http_session->num_matches && http_session->total_found >=
+ http_session->num_matches))
+ {
+ http_session->chp_finished = 1;
+ break;
+ }
+ }
+ else if (ptype_scan_counts[i] && !http_session->chp_hold_flow)
+ {
+ /* we have a scan count, but nothing in the buffer, so we should drop out of CHP */
+ http_session->chp_candidate = 0;
+ break;
+ }
+ }
+ if (!http_session->chp_candidate)
+ {
+ http_session->chp_finished = 1;
+ if (*version)
+ {
+ snort_free(*version);
+ *version = nullptr;
+ }
+ if (user)
+ {
+ snort_free(user);
+ user = nullptr;
+ }
+ for (i = 0; i < NUMBER_OF_PTYPES; i++)
+ {
+ if (nullptr != chp_rewritten[i])
+ {
+ snort_free(chp_rewritten[i]);
+ chp_rewritten[i] = nullptr;
+ }
+ }
+ 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 :
+ http_session->chp_candidate >> CHP_APPID_BITS_FOR_INSTANCE; // transform the
+ // instance into the
+ // appId.
+ if (http_session->app_type_flags & APP_TYPE_SERVICE)
+ {
+ seServiceAppIdData(session, chp_final, nullptr, version);
+ }
+
+ if (http_session->app_type_flags & APP_TYPE_CLIENT)
+ {
+ setClientAppIdData(session, chp_final, version);
+ }
+
+ if (http_session->app_type_flags & APP_TYPE_PAYLOAD)
+ {
+ setPayloadAppIdData(session, (ApplicationId)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);
+ snort_free(http_session->fflow);
+ http_session->fflow = nullptr;
+ }
+ if (*version)
+ *version = nullptr;
+ if (user)
+ {
+ session->username = user;
+ user = nullptr;
+ 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 (nullptr != chp_rewritten[i])
+ {
+ if (app_id_debug_session_flag)
+ LogMessage("AppIdDbg %s rewritten %s: %s\n", app_id_debug_session,
+ httpFieldName[i], chp_rewritten[i]);
+ if (http_session->new_field[i])
+ snort_free(http_session->new_field[i]);
+ http_session->new_field[i] = chp_rewritten[i];
+ chp_rewritten[i] = nullptr;
+ }
+ }
+ 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)
+ {
+ snort_free(user);
+ user = nullptr;
+ }
+ for (i = 0; i < NUMBER_OF_PTYPES; i++)
+ {
+ if (nullptr != chp_rewritten[i])
+ {
+ snort_free(chp_rewritten[i]);
+ chp_rewritten[i] = nullptr;
+ }
+ }
+ }
+ }
+}
+
+static inline bool payloadAppIdIsSet(AppIdData* session)
+{
+ return ( session->payloadAppId || session->tpPayloadAppId );
+}
+
+static inline void clearMiscHttpFlags(AppIdData* 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 inline int processHTTPPacket(Packet* p, AppIdData* session, int direction,
+ HttpParsedHeaders* const, const AppIdConfig* pConfig)
+{
+ Profile http_profile_context(httpPerfStats);
+ constexpr auto RESPONSE_CODE_LENGTH = 3;
+ HeaderMatchedPatterns hmp;
+ httpSession* http_session;
+ int start, end, size;
+ char* version = nullptr;
+ char* vendorVersion = nullptr;
+ char* vendor = nullptr;
+ AppId serviceAppId = 0;
+ AppId ClientAppId = 0;
+ AppId payloadAppId = 0;
+ AppId referredPayloadAppId = 0;
+ char* host;
+ char* url;
+ char* useragent;
+ char* referer;
+ char* via;
+ AppInfoTableEntry* entry;
+
+ http_session = session->hsession;
+ if (!http_session)
+ {
+ clearSessionAppIdData(session);
+ if (app_id_debug_session_flag)
+ LogMessage("AppIdDbg %s attempt to process HTTP packet with no HTTP data\n",
+ app_id_debug_session);
+
+ 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);
+
+ 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 (strlen(http_session->response_code) != RESPONSE_CODE_LENGTH)
+ {
+ /* received bad response code. Stop processing this session */
+ clearSessionAppIdData(session);
+ if (app_id_debug_session_flag)
+ LogMessage("AppIdDbg %s bad http response code\n", app_id_debug_session);
+
+ 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)
+ LogMessage("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 (pAppidActiveConfig->mod_config->instance_id)
+ checkSandboxDetection(APP_ID_HTTP);
+ }
+
+ if (app_id_debug_session_flag)
+ LogMessage("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, 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->data, p->dsize,
+ HTTP_ID_SERVER, &start, &end, &hmp, &pConfig->detectorHttpConfig) == 1))
+ {
+ if (session->serviceAppId == APP_ID_NONE || session->serviceAppId == APP_ID_HTTP)
+ {
+ RNAServiceSubtype* subtype = nullptr;
+ RNAServiceSubtype** tmpSubtype;
+
+ if (thirdparty_appid_module)
+ getServerVendorVersion((uint8_t*)session->hsession->server, strlen(
+ session->hsession->server), &vendorVersion, &vendor, &subtype);
+ else
+ getServerVendorVersion(p->data + start, end - start, &vendorVersion,
+ &vendor, &subtype);
+ if (vendor || vendorVersion)
+ {
+ if (session->serviceVendor)
+ {
+ snort_free(session->serviceVendor);
+ session->serviceVendor = nullptr;
+ }
+ if (session->serviceVersion)
+ {
+ snort_free(session->serviceVersion);
+ session->serviceVersion = nullptr;
+ }
+ 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)
+ LogMessage("AppIdDbg %s data is webdav\n", app_id_debug_session);
+ setPayloadAppIdData(session, APP_ID_WEBDAV, nullptr);
+ }
+
+ // Scan User-Agent for Browser types or Skype
+ if ((session->scan_flags & SCAN_HTTP_USER_AGENT_FLAG) && session->ClientAppId <=
+ APP_ID_NONE && useragent && (size = strlen(useragent)) > 0)
+ {
+ if (version)
+ {
+ snort_free(version);
+ version = nullptr;
+ }
+ identifyUserAgent((uint8_t*)useragent, size, &serviceAppId, &ClientAppId, &version,
+ &pConfig->detectorHttpConfig);
+ if (app_id_debug_session_flag && serviceAppId > APP_ID_NONE && serviceAppId !=
+ APP_ID_HTTP && session->serviceAppId != serviceAppId)
+ LogMessage("AppIdDbg %s User Agent is service %d\n", app_id_debug_session,
+ serviceAppId);
+ seServiceAppIdData(session, serviceAppId, nullptr, nullptr);
+ if (app_id_debug_session_flag && ClientAppId > APP_ID_NONE && ClientAppId !=
+ APP_ID_HTTP && session->ClientAppId != ClientAppId)
+ LogMessage("AppIdDbg %s User Agent is client %d\n", app_id_debug_session,
+ ClientAppId);
+ setClientAppIdData(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)
+ {
+ snort_free(version);
+ version = nullptr;
+ }
+ payloadAppId = geAppidByViaPattern((uint8_t*)via, size, &version,
+ &pConfig->detectorHttpConfig);
+ if (app_id_debug_session_flag && payloadAppId > APP_ID_NONE &&
+ session->payloadAppId != payloadAppId)
+ LogMessage("AppIdDbg %s VIA is data %d\n", app_id_debug_session,
+ payloadAppId);
+ setPayloadAppIdData(session, (ApplicationId)payloadAppId, nullptr);
+ 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->data, p->dsize,
+ HTTP_ID_X_WORKING_WITH, &start, &end, &hmp, &pConfig->detectorHttpConfig) == 1))
+ {
+ AppId 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->data + 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)
+ LogMessage("AppIdDbg %s X is client %d\n", app_id_debug_session, appId);
+ setClientAppIdData(session, appId, &version);
+ }
+ else
+ {
+ if (app_id_debug_session_flag && serviceAppId > APP_ID_NONE && serviceAppId !=
+ APP_ID_HTTP && session->serviceAppId != serviceAppId)
+ LogMessage("AppIdDbg %s X is service %d\n", app_id_debug_session, appId);
+ seServiceAppIdData(session, appId, nullptr, &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->data, p->dsize, HTTP_ID_CONTENT_TYPE, &start, &end,
+ &hmp, &pConfig->detectorHttpConfig) == 1))
+ {
+ if (thirdparty_appid_module)
+ payloadAppId = geAppidByContentType((uint8_t*)session->hsession->content_type,
+ strlen(session->hsession->content_type), &pConfig->detectorHttpConfig);
+ else
+ payloadAppId = geAppidByContentType(p->data + start, end - start,
+ &pConfig->detectorHttpConfig);
+ if (app_id_debug_session_flag && payloadAppId > APP_ID_NONE && session->payloadAppId !=
+ payloadAppId)
+ LogMessage("AppIdDbg %s Content-Type is data %d\n", app_id_debug_session,
+ payloadAppId);
+ setPayloadAppIdData(session, (ApplicationId)payloadAppId, nullptr);
+ session->scan_flags &= ~SCAN_HTTP_CONTENT_TYPE_FLAG;
+ }
+
+ if (session->scan_flags & SCAN_HTTP_HOST_URL_FLAG)
+ {
+ if (version)
+ {
+ snort_free(version);
+ version = nullptr;
+ }
+ 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)
+ LogMessage("AppIdDbg %s URL is client %d\n", app_id_debug_session,
+ ClientAppId);
+ setClientAppIdData(session, ClientAppId, nullptr);
+ }
+ if (session->serviceAppId <= APP_ID_NONE)
+ {
+ if (app_id_debug_session_flag && serviceAppId > APP_ID_NONE && serviceAppId !=
+ APP_ID_HTTP && session->serviceAppId != serviceAppId)
+ LogMessage("AppIdDbg %s URL is service %d\n", app_id_debug_session,
+ serviceAppId);
+ seServiceAppIdData(session, serviceAppId, nullptr, nullptr);
+ }
+ // DO overwrite a previously-set data
+ if (app_id_debug_session_flag && payloadAppId > APP_ID_NONE &&
+ session->payloadAppId != payloadAppId)
+ LogMessage("AppIdDbg %s URL is data %d\n", app_id_debug_session,
+ payloadAppId);
+ setPayloadAppIdData(session, (ApplicationId)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->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->clientId > APP_ID_NONE)
+ {
+ session->miscAppId = session->ClientAppId;
+ session->ClientAppId = session->payloadAppId;
+ }
+ }
+ }
+
+ clearMiscHttpFlags(session);
+ } // end DON'T skip_simple_detect
+
+ return 0;
+}
+
+static inline void stopRnaServiceInspection(Packet* p, AppIdData* session, int direction)
+{
+ if (direction == APP_ID_FROM_INITIATOR)
+ {
+ session->service_ip = *p->ptrs.ip_api.get_dst();
+ session->service_port = p->ptrs.dp;
+ }
+ else
+ {
+ session->service_ip = *p->ptrs.ip_api.get_src();
+ session->service_port = p->ptrs.sp;
+ }
+
+ session->rnaServiceState = RNA_STATE_FINISHED;
+ setAppIdFlag(session, APPID_SESSION_SERVICE_DETECTED);
+ clearAppIdFlag(session, APPID_SESSION_CONTINUE);
+}
+
+static inline bool isSslDecryptionEnabled(AppIdData* session)
+{
+ if (getAppIdFlag(session, APPID_SESSION_DECRYPTED))
+ return 1;
+// FIXIT-M J bad bad bad
+// #ifdef UNIT_TEST
+// if (session->session_packet_count >= 12)
+// return 1;
+// return 0;
+// #else
+ return session->ssn->is_proxied();
+// #endif
+}
+
+static inline void checkRestartAppDetection(AppIdData* session)
+{
+ if (getAppIdFlag(session, APPID_SESSION_DECRYPTED))
+ return;
+ if (!isSslDecryptionEnabled(session))
+ return;
+
+ AppId serviceAppId = pickServiceAppId(session);
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ isSslServiceAppId(serviceAppId);
+#else
+ bool isSsl = false;
+#endif
+
+ // 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)
+ {
+ 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)
+ LogMessage("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 pcaket.
+ if (isSsl)
+ setAppIdFlag(session, APPID_SESSION_APP_REINSPECT_SSL);
+ }
+}
+
+static inline void updateEncryptedAppId(AppIdData* session, AppId 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:
+ 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;
+ }
+}
+
+// FIXIT - H Refactor to get this to work with SSL in snort++ environment
+//static inline void ExamineSslMetadata(Packet* p, AppIdData* session, AppIdConfig* pConfig)
+static inline void ExamineSslMetadata(Packet*, AppIdData*, AppIdConfig*)
+{
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ size_t size;
+ int ret;
+ AppId ClientAppId = 0;
+ AppId payloadAppId = 0;
+
+ if ((session->scan_flags & SCAN_SSL_HOST_FLAG) && session->tsession->tls_host)
+ {
+ size = strlen(session->tsession->tls_host);
+ if ((ret = ssl_scan_hostname((const u_int8_t*)session->tsession->tls_host, size,
+ &ClientAppId, &payloadAppId, &pConfig->serviceSslConfig)))
+ {
+ setClientAppIdData(session, ClientAppId, nullptr);
+ setPayloadAppIdData(session, (ApplicationId)payloadAppId, nullptr);
+ setSSLSquelch(p, ret, (ret == 1 ? payloadAppId : ClientAppId));
+ }
+ session->scan_flags &= ~SCAN_SSL_HOST_FLAG;
+ // ret = 0;
+ }
+ if (session->tsession->tls_cname)
+ {
+ size = strlen(session->tsession->tls_cname);
+ if ((ret = ssl_scan_cname((const u_int8_t*)session->tsession->tls_cname, size,
+ &ClientAppId, &payloadAppId, &pConfig->serviceSslConfig)))
+ {
+ setClientAppIdData(session, ClientAppId, nullptr);
+ setPayloadAppIdData(session, (ApplicationId)payloadAppId, nullptr);
+ setSSLSquelch(p, ret, (ret == 1 ? payloadAppId : ClientAppId));
+ }
+ snort_free(session->tsession->tls_cname);
+ session->tsession->tls_cname = nullptr;
+ // ret = 0;
+ }
+ if (session->tsession->tls_orgUnit)
+ {
+ size = strlen(session->tsession->tls_orgUnit);
+ if ((ret = ssl_scan_cname((const u_int8_t*)session->tsession->tls_orgUnit, size,
+ &ClientAppId, &payloadAppId, &pConfig->serviceSslConfig)))
+ {
+ setClientAppIdData(session, ClientAppId, nullptr);
+ setPayloadAppIdData(session, (ApplicationId)payloadAppId, nullptr);
+ setSSLSquelch(p, ret, (ret == 1 ? payloadAppId : ClientAppId));
+ }
+ snort_free(session->tsession->tls_orgUnit);
+ session->tsession->tls_orgUnit = nullptr;
+ // ret = 0;
+ }
+#endif
+}
+
+static inline int RunClientDetectors(AppIdData* session,
+ Packet* p,
+ int direction,
+ AppIdConfig* pConfig)
+{
+ int ret = CLIENT_APP_INPROCESS;
+
+ if (session->clientData != nullptr)
+ {
+ ret = session->clientData->validate(p->data, p->dsize, direction,
+ session, p, session->clientData->userData, pConfig);
+ if (app_id_debug_session_flag)
+ LogMessage("AppIdDbg %s %s client detector returned %d\n", app_id_debug_session,
+ session->clientData->name ? session->clientData->name : "UNKNOWN", ret);
+ }
+ else if ( (session->candidate_client_list != nullptr)
+ && (sflist_count(session->candidate_client_list) > 0) )
+ {
+ SF_LNODE* node;
+ RNAClientAppModule* client;
+
+ ret = CLIENT_APP_INPROCESS;
+ (void)sflist_first(session->candidate_client_list, &node);
+ while (node != nullptr)
+ {
+ int result;
+ SF_LNODE* node_tmp;
+
+ client = (RNAClientAppModule*)node->ndata;
+ result = client->validate(p->data, p->dsize, direction,
+ session, p, client->userData, pConfig);
+ if (app_id_debug_session_flag)
+ LogMessage("AppIdDbg %s %s client detector returned %d\n", app_id_debug_session,
+ client->name ? client->name : "UNKNOWN", result);
+
+ node_tmp = node;
+ sflist_next(&node);
+ if (result == CLIENT_APP_SUCCESS)
+ {
+ ret = CLIENT_APP_SUCCESS;
+ session->clientData = client;
+ sflist_free(session->candidate_client_list);
+ session->candidate_client_list = nullptr;
+ break; /* done */
+ }
+ else if (result != CLIENT_APP_INPROCESS) /* fail */
+ {
+ sflist_remove_node(session->candidate_client_list, node_tmp);
+ }
+ }
+ }
+
+ return ret;
+}
+
+static inline void getOffsetsFromRebuilt(Packet* pkt, httpSession* hsession)
+{
+// size of "GET /\r\n\r\n"
+#define MIN_HTTP_REQ_HEADER_SIZE 9
+ const uint8_t cookieStr[] = "Cookie: ";
+ unsigned cookieStrLen = sizeof(cookieStr)-1;
+ const uint8_t crlf[] = "\r\n";
+ unsigned crlfLen = sizeof(crlf)-1;
+ const uint8_t crlfcrlf[] = "\r\n\r\n";
+ unsigned crlfcrlfLen = sizeof(crlfcrlf)-1;
+ const uint8_t* p;
+ uint8_t* headerEnd;
+ uint16_t headerSize;
+
+ if (!pkt || !pkt->data || pkt->dsize < MIN_HTTP_REQ_HEADER_SIZE)
+ return;
+
+ p = pkt->data;
+
+ if (!(headerEnd = (uint8_t*)service_strstr(p, pkt->dsize, crlfcrlf, crlfcrlfLen)))
+ return;
+
+ headerEnd += crlfcrlfLen;
+
+ headerSize = headerEnd - p;
+
+ //uri offset is the index of the first char after the first space in the data
+ if (!(p = (uint8_t*)memchr(pkt->data, ' ', headerSize)))
+ return;
+ hsession->uriOffset = ++p - pkt->data;
+ headerSize = headerEnd - p;
+
+ //uri end offset is the index of the first CRLF sequence after uri offset
+ if (!(p = (uint8_t*)service_strstr(p, headerSize, crlf, crlfLen)))
+ {
+ // clear uri offset if we can't find an end offset
+ hsession->uriOffset = 0;
+ return;
+ }
+ hsession->uriEndOffset = p - pkt->data;
+ headerSize = headerEnd - p;
+
+ //cookie offset is the index of the first char after the cookie header, "Cookie: "
+ if (!(p = (uint8_t*)service_strstr(p, headerSize, cookieStr, cookieStrLen)))
+ return;
+ hsession->cookieOffset = p + cookieStrLen - pkt->data;
+ headerSize = headerEnd - p;
+
+ //cookie end offset is the index of the first CRLF sequence after cookie offset
+ if (!(p = (uint8_t*)service_strstr(p, headerSize, crlf, crlfLen)))
+ {
+ // clear cookie offset if we can't find a cookie end offset
+ hsession->cookieOffset = 0;
+ return;
+ }
+ hsession->cookieEndOffset = p - pkt->data;
+}
+
+static int16_t snortId_for_ftp;
+static int16_t snortId_for_ftp_data;
+static int16_t snortId_for_imap;
+static int16_t snortId_for_pop3;
+static int16_t snortId_for_smtp;
+static int16_t snortId_for_http2;
+
+static inline void synchAppIdWithSnortId(AppId newAppId, Packet* p, AppIdData* session,
+ AppIdConfig* pConfig)
+{
+ AppInfoTableEntry* entry;
+ int16_t tempSnortId = session->snortId;
+
+ if (tempSnortId == UNSYNCED_SNORT_ID)
+ {
+ tempSnortId = session->snortId = p->flow->ssn_state.application_protocol;
+ }
+
+ if (tempSnortId == snortId_for_ftp || tempSnortId == snortId_for_ftp_data ||
+ tempSnortId == snortId_for_imap || tempSnortId == snortId_for_pop3 ||
+ tempSnortId == snortId_for_smtp || tempSnortId == snortId_for_http2)
+ {
+ return; // These preprocessors, in snort proper, already know and expect these to remain
+ // unchanged.
+ }
+ if ((entry = appInfoEntryGet(newAppId, pConfig)) && (tempSnortId = entry->snortId))
+ {
+ // Snort has a separate protocol ID for HTTP/2. We don't. So, when we
+ // talk to them about it, we have to play by their rules.
+ if ((newAppId == APP_ID_HTTP) && (session->is_http2))
+ tempSnortId = snortId_for_http2;
+
+ if (tempSnortId != session->snortId)
+ {
+ if (app_id_debug_session_flag)
+ if (tempSnortId == snortId_for_http2)
+ LogMessage("AppIdDbg %s Telling Snort that it's HTTP/2\n",
+ app_id_debug_session);
+
+ p->flow->ssn_state.application_protocol = tempSnortId;
+ session->snortId = tempSnortId;
+ }
+ }
+}
+
+static inline void checkTerminateTpModule(uint16_t tpPktCount, AppIdData* session)
+{
+ if ((tpPktCount >= pAppidActiveConfig->mod_config->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->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;
+ LogMessage("AppIdDbg \n");
+ LogMessage("AppIdDbg ------------------------------------------------\n");
+ LogMessage("AppIdDbg \n");
+ if (p->data != nullptr && p->dsize)
+ {
+ tweakedPayload= (char*)snort_calloc((CHAR_DUMP_WIDTH*2)+1); // room for hex
+ if (tweakedPayload)
+ {
+ int j;
+ int i;
+ LogMessage("AppIdDbg data: (%d chars per line)\n",CHAR_DUMP_WIDTH);
+ for (j=0; j<p->dsize; j+=CHAR_DUMP_WIDTH)
+ {
+ for (i=j; i<p->dsize && i<(j+CHAR_DUMP_WIDTH); i++)
+ {
+ if ((int)p->data[i] >= 32 && (int)p->data[i] <=126)
+ tweakedPayload[i-j] = p->data[i];
+ else
+ tweakedPayload[i-j] = '.';
+ }
+ tweakedPayload[i-j] = '\0';
+ LogMessage("AppIdDbg %s\n", tweakedPayload);
+ }
+//#define DUMP_IN_HEX
+#ifdef DUMP_IN_HEX
+ LogMessage("AppIdDbg HEX data: (%d chars per line)\n",CHAR_DUMP_WIDTH);
+ for (j=0; j<p->dsize; j+=CHAR_DUMP_WIDTH)
+ {
+ for (i=j; i<p->dsize && i<(j+CHAR_DUMP_WIDTH); i++)
+ {
+ sprintf(&tweakedPayload[(i-j)*2], "%02x", (p->data)[i]);
+ }
+ // terminating '\0' provided by sprintf()
+ LogMessage("AppIdDbg %s\n", tweakedPayload);
+ }
+#endif
+ snort_free(tweakedPayload); tweakedPayload = nullptr;
+ }
+ }
+ if (p->flow)
+ {
+ LogMessage(
+ "AppIdDbg \nAppIdDbg for p->flow=%p is_session_decrypted=%d direction=%d\n",
+ p->flow, _dpd.streamAPI->is_session_decrypted(p->flow),
+ _dpd.sessionAPI->get_ignore_direction(p->flow));
+ }
+
+ LogMessage("AppIdDbg ptrs.sp: %d\n", p->ptrs.sp);
+ LogMessage("AppIdDbg ptrs.dp: %d\n", p->ptrs.dp);
+ LogMessage("AppIdDbg orig_src_port: %d\n", p->orig_src_port);
+ LogMessage("AppIdDbg rig_dst_port: %d\n", p->orig_dst_port);
+ LogMessage("AppIdDbg payloadsize: %d\n", p->dsize);
+
+ if ((p->packet_flags) & 0x00000080)
+ {
+ LogMessage("AppIdDbg direction: client\n");
+ }
+ else if ((p->packet_flags) & 0x00000040)
+ {
+ LogMessage("AppIdDbg direction: server\n");
+ }
+ else
+ {
+ LogMessage("AppIdDbg direction: unknown\n");
+ }
+
+ if ((p->packet_flags) & 0x00000001)
+ LogMessage("AppIdDbg A rebuilt fragment\n");
+ if ((p->packet_flags) & 0x00000002)
+ LogMessage("AppIdDbg A rebuilt stream\n");
+ if ((p->packet_flags) & 0x00000004)
+ LogMessage(
+ "AppIdDbg From an unestablished stream, traffic send one direction only\n");
+ if ((p->packet_flags) & 0x00000008)
+ LogMessage("AppIdDbg From an established stream\n");
+ if ((p->packet_flags) & 0x00000010)
+ LogMessage("AppIdDbg this packet has been queued for stream reassembly\n");
+ if ((p->packet_flags) & 0x00000020)
+ LogMessage("AppIdDbg packet completes the 3 way handshake\n");
+ if ((p->packet_flags) & 0x00000040)
+ LogMessage("AppIdDbg packet come from server side of a connection(tcp)\n");
+ if ((p->packet_flags) & 0x00000080)
+ LogMessage("AppIdDbg packet come from client side of a connection(tcp)\n");
+ if ((p->packet_flags) & 0x00000100)
+ LogMessage("AppIdDbg start of PDU\n");
+ if ((p->packet_flags) & 0x00000200)
+ LogMessage("AppIdDbg end of PDU\n");
+ if ((p->packet_flags) & 0x00800000)
+ LogMessage("AppIdDbg packet has new size\n");
+ }
+}
+
+#else
+#define printSnortPacket(SFSnortPacket_ptr)
+#endif
+
+void fwAppIdInit()
+{
+ /* init globals for snortId compares */
+ snortId_for_ftp = FindProtocolReference("ftp");
+ snortId_for_ftp_data = FindProtocolReference("ftp-data");
+ snortId_for_imap = FindProtocolReference("imap");
+ snortId_for_pop3 = FindProtocolReference("pop3");
+ snortId_for_smtp = FindProtocolReference("smtp");
+ snortId_for_http2 = FindProtocolReference("http2");
+}
+
+void fwAppIdSearch(Packet* p)
+{
+ AppIdData* session;
+ IpProtocol protocol;
+ AppId tpAppId = 0;
+ AppId serviceAppId = 0;
+ AppId ClientAppId = 0;
+ AppId payloadAppId = 0;
+ bool isTpAppidDiscoveryDone = false;
+ uint64_t flow_flags;
+ int direction;
+ const sfip_t* ip;
+ uint16_t port;
+ size_t size;
+ int tp_confidence;
+ AppId* tp_proto_list;
+ ThirdPartyAppIDAttributeData* tp_attribute_data;
+ AppInfoTableEntry* entry;
+ AppIdConfig* pConfig = pAppidActiveConfig;
+ bool is_decrypted;
+ bool is_rebuilt;
+ bool is_http2;
+
+ app_id_raw_packet_count++;
+
+ if (!p->flow)
+ {
+ app_id_ignored_packet_count++;
+ return;
+ }
+
+ is_decrypted = p->flow->is_proxied();
+ is_rebuilt = p->is_rebuilt();
+// FIXIT-M - Need to convert this _dpd stream api call to the correct snort++ method
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ is_http2 = _dpd.streamAPI->is_session_http2(p->flow);
+#else
+ is_http2 = false;
+#endif
+ if (is_http2)
+ {
+ session = getAppIdData(p->flow);
+ if (session)
+ session->is_http2 = true;
+ if (!is_rebuilt)
+ {
+ // 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 (is_rebuilt && !is_decrypted)
+ {
+ session = getAppIdData(p->flow);
+ if (session && session->hsession && session->hsession->get_offsets_from_rebuilt)
+ {
+ getOffsetsFromRebuilt(p, session->hsession);
+ if (app_id_debug_session_flag)
+ LogMessage(
+ "AppIdDbg %s offsets from rebuilt packet: uri: %u-%u cookie: %u-%u\n",
+ app_id_debug_session, session->hsession->uriOffset,
+ session->hsession->uriEndOffset, session->hsession->cookieOffset,
+ session->hsession->cookieEndOffset);
+ }
+ app_id_ignored_packet_count++;
+ return;
+ }
+ }
+
+ session = appSharedGetData(p);
+ if (session)
+ {
+ if (session->common.fsf_type.flow_type == APPID_SESSION_TYPE_IGNORE)
+ return;
+ if (session->common.fsf_type.flow_type == APPID_SESSION_TYPE_NORMAL)
+ {
+ protocol = session->proto;
+ session->ssn = p->flow;
+ }
+ else if (p->is_tcp())
+ protocol = IpProtocol::TCP;
+ else
+ protocol = IpProtocol::UDP;
+
+ ip = p->ptrs.ip_api.get_src();
+ if (session->common.initiator_port)
+ direction = (session->common.initiator_port == p->ptrs.sp) ? APP_ID_FROM_INITIATOR :
+ APP_ID_FROM_RESPONDER;
+ else
+ direction = (sfip_fast_equals_raw(ip, &session->common.initiator_ip)) ?
+ APP_ID_FROM_INITIATOR : APP_ID_FROM_RESPONDER;
+ }
+ else
+ {
+ if (p->is_tcp())
+ protocol = IpProtocol::TCP;
+
+ else if (p->is_udp())
+ protocol = IpProtocol::UDP;
+
+ else if ( p->is_ip4() || p->is_ip6() )
+ protocol = p->get_ip_proto_next();
+
+ else
+ return;
+
+ // FIXIT - L Refactor to use direction symbols defined by snort proper
+ direction = p->is_from_client() ? APP_ID_FROM_INITIATOR : APP_ID_FROM_RESPONDER;
+ }
+
+ app_id_debug_session_flag = fwAppIdDebugCheck(p->flow, session, app_id_debug_flag,
+ &app_id_debug_info, app_id_debug_session, direction);
+ //if (app_id_debug_session_flag)
+ // LogMessage("AppIdDbg %s flag:%08x,decrypt:%s,http2:%s\n", app_id_debug_session,
+ // p->packet_flags, is_decrypted?"T":"F", is_http2?"T":"F");
+
+ // 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)
+ {
+ // FIXIT-M: This _dpd call needs to be convert to correct snort++ call
+ // static THREAD_LOCAL APPID_SESSION_STRUCT_FLAG ignore_fsf {
+ // APPID_SESSION_TYPE_IGNORE };
+
+ // _dpd.sessionAPI->set_application_data(p->flow, PP_APP_ID, &ignore_fsf,
+ // nullptr);
+ if (app_id_debug_session_flag)
+ LogMessage("AppIdDbg %s not monitored\n", app_id_debug_session);
+ }
+ else
+ {
+ AppIdData* tmp_session;
+
+ tmp_session = (decltype(tmp_session))snort_calloc(sizeof(AppIdData));
+
+ tmp_session->common.fsf_type.flow_type = APPID_SESSION_TYPE_TMP;
+ tmp_session->common.flags = flow_flags;
+ ip = (direction == APP_ID_FROM_INITIATOR) ?
+ p->ptrs.ip_api.get_src() : p->ptrs.ip_api.get_dst();
+ tmp_session->common.initiator_ip = *ip;
+ if ( (protocol == IpProtocol::TCP || protocol == IpProtocol::UDP ) &&
+ p->ptrs.sp != p->ptrs.dp )
+ {
+ tmp_session->common.initiator_port = (direction == APP_ID_FROM_INITIATOR) ?
+ p->ptrs.sp : p->ptrs.dp;
+ }
+ else
+ tmp_session->common.initiator_port = 0;
+ tmp_session->common.policyId = appIdPolicyId;
+ p->flow->set_application_data(tmp_session);
+ if (app_id_debug_session_flag)
+ LogMessage("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)
+ LogMessage("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 (app_id_debug_session_flag)
+ LogMessage("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->pkth->pktlen;
+ else
+ session->stats.responderBytes += p->pkth->pktlen;
+
+ session->common.flags = flow_flags;
+ session->common.policyId = appIdPolicyId;
+
+ tpAppId = session->tpAppId;
+
+ session->common.policyId = appIdPolicyId;
+
+ 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);
+ LogMessage("AppIdDbg %s Ignoring connection with service %d\n",
+ app_id_debug_session, session->serviceAppId);
+ }
+ return;
+ }
+
+ if (p->packet_flags & PKT_STREAM_ORDER_BAD)
+ setAppIdFlag(session, APPID_SESSION_OOO);
+
+ else if ( p->is_tcp() && p->ptrs.tcph )
+ {
+ const auto* tcph = p->ptrs.tcph;
+ if ( tcph->is_rst() && session->previous_tcp_flags == TH_SYN )
+ {
+ AppIdServiceIDState* id_state;
+
+ setAppIdFlag(session, APPID_SESSION_SYN_RST);
+ if (sfip_is_set(&session->service_ip))
+ {
+ ip = &session->service_ip;
+ port = session->service_port;
+ }
+ else
+ {
+ ip = p->ptrs.ip_api.get_src();
+ port = p->ptrs.sp;
+ }
+
+ id_state = AppIdGetServiceIDState(ip, IpProtocol::TCP, port,
+ AppIdServiceDetectionLevel(
+ session));
+
+ if (id_state)
+ {
+ if (!id_state->reset_time)
+ id_state->reset_time = packet_time();
+ else if ((packet_time() - id_state->reset_time) >= 60)
+ {
+ // FIXIT-H ip's on the packet are const
+ // AppIdRemoveServiceIDState(ip, IpProtocol::TCP, port,
+ // AppIdServiceDetectionLevel(session));
+ setAppIdFlag(session, APPID_SESSION_SERVICE_DELETED);
+ }
+ }
+ }
+
+ session->previous_tcp_flags = p->ptrs.tcph->th_flags;
+ }
+
+ /*HostPort based AppId. */
+ if (!(session->scan_flags & SCAN_HOST_PORT_FLAG))
+ {
+ HostPortVal* hv;
+
+ session->scan_flags |= SCAN_HOST_PORT_FLAG;
+ if (direction == APP_ID_FROM_INITIATOR)
+ {
+ ip = p->ptrs.ip_api.get_dst();
+ port = p->ptrs.dp;
+ }
+ else
+ {
+ ip = p->ptrs.ip_api.get_src();
+ port = p->ptrs.sp;
+ }
+
+ if ((hv = hostPortAppCacheFind(ip, port, protocol, pConfig)))
+ {
+ switch (hv->type)
+ {
+ case 1:
+ session->ClientAppId = hv->appId;
+ session->rnaClientState = RNA_STATE_FINISHED;
+ break;
+ case 2:
+ session->payloadAppId = hv->appId;
+ break;
+ default:
+ 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);
+ session->tpsession = nullptr;
+ }
+ }
+ }
+
+ checkRestartAppDetection(session);
+
+ //restart inspection by 3rd party
+ if (!getAppIdFlag(session, APPID_SESSION_NO_TPI))
+ {
+ if (TPIsAppIdDone(session->tpsession) && getAppIdFlag(session,
+ APPID_SESSION_HTTP_SESSION) && p->dsize)
+ {
+ if (session->tpReinspectByInitiator)
+ {
+ clearAppIdFlag(session, APPID_SESSION_APP_REINSPECT);
+ if (direction == APP_ID_FROM_RESPONDER)
+ session->tpReinspectByInitiator = 0; //toggle at OK response
+ }
+ else if (direction == APP_ID_FROM_INITIATOR)
+ {
+ session->tpReinspectByInitiator = 1; //once per request
+ setAppIdFlag(session, APPID_SESSION_APP_REINSPECT);
+ if (app_id_debug_session_flag)
+ LogMessage("AppIdDbg %s 3rd party allow reinspect http\n",
+ app_id_debug_session);
+ session->appHttpFieldClear();
+ }
+ }
+ }
+
+ 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 ( p->ptrs.ip_api.tos() == 8 )
+ {
+ session->payloadAppId = APP_ID_SFTP;
+ if (app_id_debug_session_flag)
+ LogMessage("AppIdDbg %s data is SFTP\n", app_id_debug_session);
+ }
+ }
+
+ Profile tpPerfStats_profile_context(tpPerfStats);
+
+ /*** Start of third-party processing. ***/
+ if (thirdparty_appid_module &&
+ !getAppIdFlag(session, APPID_SESSION_NO_TPI) &&
+ (!TPIsAppIdDone(session->tpsession) ||
+ getAppIdFlag(session, APPID_SESSION_APP_REINSPECT | APPID_SESSION_APP_REINSPECT_SSL)))
+ {
+ // 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);
+ }
+ if (p->dsize || pAppidActiveConfig->mod_config->tp_allow_probes)
+ {
+ if (protocol != IpProtocol::TCP || !p->dsize || (p->packet_flags &
+ PKT_STREAM_ORDER_OK) ||
+ pAppidActiveConfig->mod_config->tp_allow_probes)
+ {
+ {
+ Profile tpLibPerfStats_profile_context(tpLibPerfStats);
+ if (!session->tpsession)
+ {
+ if (!(session->tpsession = thirdparty_appid_module->session_create()))
+ FatalError(
+ "Could not allocate AppIdData->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);
+ }
+
+ 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)
+ LogMessage("AppIdDbg %s 3rd party returned %d\n", app_id_debug_session,
+ tpAppId);
+
+ if (appInfoEntryFlagGet(tpAppId, APPINFO_FLAG_IGNORE, pConfig))
+ {
+ if (app_id_debug_session_flag)
+ LogMessage("AppIdDbg %s 3rd party ignored\n", app_id_debug_session);
+ if (getAppIdFlag(session, APPID_SESSION_HTTP_SESSION))
+ tpAppId = APP_ID_HTTP;
+ else
+ tpAppId = APP_ID_NONE;
+ }
+
+ // 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)
+ LogMessage("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
+ if (appInfoEntryFlagGet(tpAppId, APPINFO_FLAG_TP_CLIENT, pAppidActiveConfig))
+ session->ClientAppId = tpAppId;
+
+ ProcessThirdPartyResults(p, session, tp_confidence, tp_proto_list,
+ tp_attribute_data);
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ if (getAppIdFlag(session, APPID_SESSION_SSL_SESSION) &&
+ !(session->scan_flags & SCAN_SSL_HOST_FLAG))
+ {
+ setSSLSquelch(p, 1, tpAppId);
+ }
+#endif
+ }
+ else
+ {
+ tpAppId = APP_ID_NONE;
+ if (app_id_debug_session_flag && !getAppIdFlag(session,
+ APPID_SESSION_TPI_OOO_LOGGED))
+ {
+ setAppIdFlag(session, APPID_SESSION_TPI_OOO_LOGGED);
+ LogMessage("AppIdDbg %s 3rd party packet out-of-order\n",
+ app_id_debug_session);
+ }
+ }
+
+ 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);
+ }
+
+ if (tpAppId == APP_ID_SSL &&
+ (stream.get_application_protocol_id(p->flow) == 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;
+ }
+
+ if (tpAppId > APP_ID_NONE && (!getAppIdFlag(session, APPID_SESSION_APP_REINSPECT) ||
+ session->payloadAppId > APP_ID_NONE))
+ {
+ AppId snorAppId;
+
+ // if the packet is HTTP, then search for via pattern
+ if (getAppIdFlag(session, APPID_SESSION_HTTP_SESSION) && session->hsession)
+ {
+ snorAppId = APP_ID_HTTP;
+ //data should never be APP_ID_HTTP
+ if (tpAppId != APP_ID_HTTP)
+ session->tpPayloadAppId = tpAppId;
+
+ session->tpAppId = APP_ID_HTTP;
+
+ processHTTPPacket(p, session, direction, nullptr, pAppidActiveConfig);
+
+ 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 (direction == APP_ID_FROM_INITIATOR)
+ {
+ ip = p->ptrs.ip_api.get_dst();
+ session->service_ip = *ip;
+ session->service_port = p->ptrs.dp;
+ }
+ else
+ {
+ ip = p->ptrs.ip_api.get_src();
+ session->service_ip = *ip;
+ session->service_port = p->ptrs.sp;
+ }
+ }
+ }
+ else if (getAppIdFlag(session, APPID_SESSION_SSL_SESSION) && session->tsession)
+ {
+ ExamineSslMetadata(p, session, pConfig);
+
+ uint16_t serverPort;
+ AppId porAppId;
+
+ serverPort = (direction == APP_ID_FROM_INITIATOR) ? p->ptrs.dp : p->ptrs.sp;
+
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ porAppId = getSslServiceAppId(serverPort);
+#else
+ porAppId = serverPort;
+#endif
+ if (tpAppId == APP_ID_SSL )
+ {
+ tpAppId = porAppId;
+
+ //SSL policy needs to determine IMAPS/POP3S etc before appId sees first
+ // server packet
+ session->portServiceAppId = porAppId;
+
+ if (app_id_debug_session_flag)
+ LogMessage("AppIdDbg %s SSL is service %d, portServiceAppId %d\n",
+ app_id_debug_session, tpAppId, session->portServiceAppId);
+ }
+ else
+ {
+ session->tpPayloadAppId = tpAppId;
+ tpAppId = porAppId;
+ if (app_id_debug_session_flag)
+ LogMessage("AppIdDbg %s SSL is %d\n", app_id_debug_session, tpAppId);
+ }
+ session->tpAppId = tpAppId;
+ snorAppId = APP_ID_SSL;
+ }
+ else
+ {
+ //for non-http protocols, tp id is treated like serviceId
+
+ snorAppId = tpAppId;
+ session->tpAppId = tpAppId;
+ }
+
+ synchAppIdWithSnortId(snorAppId, p, session, pConfig);
+ }
+ else
+ {
+ if ( protocol != IpProtocol::TCP || !p->dsize ||
+ (p->packet_flags & (PKT_STREAM_ORDER_OK | PKT_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);
+ }
+ }
+ }
+ }
+ }
+
+ if (direction == APP_ID_FROM_RESPONDER && !getAppIdFlag(session,
+ APPID_SESSION_PORT_SERVICE_DONE|APPID_SESSION_SYN_RST))
+ {
+ setAppIdFlag(session, APPID_SESSION_PORT_SERVICE_DONE);
+ session->portServiceAppId = getPortServiceId(protocol, p->ptrs.sp, pConfig);
+ if (app_id_debug_session_flag)
+ LogMessage("AppIdDbg %s port service %d\n", app_id_debug_session,
+ session->portServiceAppId);
+ }
+
+ /* 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 data (we'll ignore 0-sized packets in sequencing). */
+ if ( (session->portServiceAppId <= APP_ID_NONE)
+ && (session->length_sequence.sequence_cnt < LENGTH_SEQUENCE_CNT_MAX)
+ && (p->dsize > 0))
+ {
+ 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->dsize;
+ 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 (session->serviceAppId == APP_ID_DNS &&
+ pAppidActiveConfig->mod_config->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(session, ClientAppId, nullptr);
+ }
+ else if (session->serviceAppId == APP_ID_RTMP)
+ ExamineRtmpMetadata(session);
+ else if (getAppIdFlag(session, APPID_SESSION_SSL_SESSION) && session->tsession)
+ ExamineSslMetadata(p, session, pConfig);
+ }
+ else if (protocol != IpProtocol::TCP || !p->dsize || (p->packet_flags & PKT_STREAM_ORDER_OK))
+ {
+ /*** Start of service discovery. ***/
+ if (session->rnaServiceState != RNA_STATE_FINISHED)
+ {
+ Profile serviceMatchPerfStats_profile_context(serviceMatchPerfStats);
+
+ uint32_t prevRnaServiceState;
+
+ 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 sesssion.
+ if (session->rnaServiceState == RNA_STATE_NONE && p->dsize)
+ {
+ if ( p->flow->get_session_flags() & SSNFLAG_MIDSTREAM )
+ {
+ // Unless it could be ftp control
+ if ( protocol == IpProtocol::TCP && (p->ptrs.sp == 21 || p->ptrs.dp == 21) &&
+ !(p->ptrs.tcph->is_fin() || p->ptrs.tcph->is_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->ptrs.dp != 21)
+ setAppIdFlag(session, APPID_SESSION_RESPONDER_SEEN);
+ }
+ session->rnaServiceState = RNA_STATE_STATEFUL;
+ }
+ else
+ {
+ setAppIdFlag(session, APPID_SESSION_MID | APPID_SESSION_SERVICE_DETECTED);
+ session->rnaServiceState = RNA_STATE_FINISHED;
+ }
+ }
+ 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.
+ if ((entry = appInfoEntryGet(tpAppId, pConfig))
+ && entry->svrValidator &&
+ ((entry->flags & APPINFO_FLAG_SERVICE_ADDITIONAL) ||
+ ((entry->flags & APPINFO_FLAG_SERVICE_UDP_REVERSED) && protocol ==
+ IpProtocol::UDP &&
+ getAppIdFlag(session, APPID_SESSION_INITIATOR_MONITORED |
+ APPID_SESSION_RESPONDER_MONITORED))))
+ {
+ AppIdFlowdataDeleteAllByMask(session,
+ APPID_SESSION_DATA_SERVICE_MODSTATE_BIT);
+
+ session->serviceData = entry->svrValidator;
+ session->rnaServiceState = RNA_STATE_STATEFUL;
+ }
+ else
+ {
+ stopRnaServiceInspection(p, session, direction);
+ }
+ }
+ 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)
+ {
+ entry = appInfoEntryGet(tpAppId, pConfig);
+
+ if (entry && entry->svrValidator && !(entry->flags &
+ APPINFO_FLAG_SERVICE_ADDITIONAL))
+ {
+ if (app_id_debug_session_flag)
+ LogMessage("AppIdDbg %s Stopping service detection\n",
+ app_id_debug_session);
+ stopRnaServiceInspection(p, session, direction);
+ }
+ }
+
+ if (session->rnaServiceState == RNA_STATE_STATEFUL)
+ {
+ 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 (session->serviceAppId == APP_ID_DNS &&
+ pAppidActiveConfig->mod_config->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(session, ClientAppId, nullptr);
+ }
+ else if (session->serviceAppId == APP_ID_RTMP)
+ ExamineRtmpMetadata(session);
+ else if (getAppIdFlag(session, APPID_SESSION_SSL_SESSION) && session->tsession)
+ ExamineSslMetadata(p, session, pConfig);
+
+ 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);
+ }
+ }
+ }
+ /*** End of service discovery. ***/
+
+ /*** Start of client discovery. ***/
+ if (session->rnaClientState != RNA_STATE_FINISHED)
+ {
+ Profile clientMatchPerfStats_profile_context(clientMatchPerfStats);
+ uint32_t prevRnaClientState = session->rnaClientState;
+ bool was_http2 = session->is_http2;
+ bool was_service = getAppIdFlag(session, APPID_SESSION_SERVICE_DETECTED) ? true :
+ false;
+
+ //decision to directly call validator or go through elaborate service_state tracking
+ //is made once at the beginning of sesssion.
+ if (session->rnaClientState == RNA_STATE_NONE && p->dsize && direction ==
+ APP_ID_FROM_INITIATOR)
+ {
+ if ( p->flow->get_session_flags() & SSNFLAG_MIDSTREAM )
+ session->rnaClientState = RNA_STATE_FINISHED;
+
+ else if (TPIsAppIdAvailable(session->tpsession) && (tpAppId = session->tpAppId) >
+ APP_ID_NONE && tpAppId < SF_APPID_MAX)
+ {
+ AppInfoTableEntry* entry;
+
+ 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;
+ }
+ }
+ 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)
+ {
+ 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);
+ }
+ }
+
+ if (session->rnaClientState == RNA_STATE_DIRECT)
+ {
+ int ret = CLIENT_APP_INPROCESS;
+
+ 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);
+ }
+
+ switch (ret)
+ {
+ case CLIENT_APP_INPROCESS:
+ break;
+ default:
+ session->rnaClientState = RNA_STATE_FINISHED;
+ break;
+ }
+ }
+ else if (session->rnaClientState == RNA_STATE_STATEFUL)
+ {
+ AppIdDiscoverClientApp(p, direction, session, pConfig);
+ isTpAppidDiscoveryDone = true;
+ if (session->candidate_client_list != nullptr)
+ {
+ 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);
+ }
+ else
+ {
+ setAppIdFlag(session, APPID_SESSION_CLIENT_DETECTED);
+ }
+ }
+ }
+ if (app_id_debug_session_flag)
+ if (!was_http2 && session->is_http2)
+ LogMessage("AppIdDbg %s Got a preface for HTTP/2\n", app_id_debug_session);
+
+ if (!was_service && getAppIdFlag(session, APPID_SESSION_SERVICE_DETECTED))
+ synchAppIdWithSnortId(session->serviceAppId, p, session, pConfig);
+ }
+ /*** End of client discovery. ***/
+
+ setAppIdFlag(session, APPID_SESSION_ADDITIONAL_PACKET);
+ }
+ else
+ {
+ if (app_id_debug_session_flag && p->dsize &&
+ !getAppIdFlag(session, APPID_SESSION_OOO_LOGGED))
+ {
+ setAppIdFlag(session, APPID_SESSION_OOO_LOGGED);
+ LogMessage("AppIdDbg %s packet out-of-order\n", app_id_debug_session);
+ }
+ }
+
+ serviceAppId = pickServiceAppId(session);
+ payloadAppId = pickPayloadId(session);
+
+ if (serviceAppId > APP_ID_NONE)
+ {
+ if (getAppIdFlag(session, APPID_SESSION_DECRYPTED))
+ {
+ if (session->miscAppId == APP_ID_NONE)
+ updateEncryptedAppId(session, serviceAppId);
+ }
+// FIXIT-M: Need to determine what api to use for this _dpd function
+#if 1
+ UNUSED(isTpAppidDiscoveryDone);
+#else
+ else if (isTpAppidDiscoveryDone && isSslServiceAppId(serviceAppId) &&
+ _dpd.isSSLPolicyEnabled(nullptr))
+ setAppIdFlag(session, APPID_SESSION_CONTINUE);
+#endif
+ }
+
+// FIXIT-M: Need to determine what api to use for this _dpd function
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ _dpd.streamAPI->set_application_id(p->flow, serviceAppId, pickClientAppId(session),
+ payloadAppId, pickMiscAppId(session));
+#endif
+
+ /* 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)
+ {
+ const 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: typeString = "unknown"; break;
+ }
+
+ LogMessage("AppIdDbg %s appId: %u (safe)search_support_type=%s\n",
+ app_id_debug_session, payloadAppId, typeString);
+ }
+ }
+
+ if ( serviceAppId != APP_ID_NONE )
+ {
+ if ( payloadAppId != APP_ID_NONE && payloadAppId != session->pastIndicator)
+ {
+ session->pastIndicator = payloadAppId;
+ checkSessionForAFIndicator(p, direction, pConfig, (ApplicationId)payloadAppId);
+ }
+
+ if (session->payloadAppId == APP_ID_NONE && session->pastForecast != serviceAppId &&
+ session->pastForecast != APP_ID_UNKNOWN)
+ {
+ session->pastForecast = checkSessionForAFForecast(session, p, direction, pConfig,
+ (ApplicationId)serviceAppId);
+ }
+ }
+}
+
+static inline void pickHttpXffAddress(Packet*, AppIdData* appIdSession,
+ ThirdPartyAppIDAttributeData* attribute_data)
+{
+ int i;
+ static const 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.
+// FIXIT-M:
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ if (!appIdSession->hsession->xffPrecedence)
+ appIdSession->hsession->xffPrecedence = _dpd.sessionAPI->get_http_xff_precedence(
+ p->flow, p->packet_flags, &appIdSession->hsession->numXffFields);
+#endif
+
+ if (!appIdSession->hsession->xffPrecedence)
+ {
+ appIdSession->hsession->xffPrecedence = defaultXffPrecedence;
+ appIdSession->hsession->numXffFields = sizeof(defaultXffPrecedence) /
+ sizeof(defaultXffPrecedence[0]);
+ }
+
+ if (app_id_debug_session_flag)
+ {
+ for (i = 0; i < attribute_data->numXffFields; i++)
+ LogMessage("AppIdDbg %s %s : %s\n", app_id_debug_session,
+ attribute_data->xffFieldValue[i].field, attribute_data->xffFieldValue[i].value);
+ }
+
+ // 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)
+ sfip_free(appIdSession->hsession->xffAddr);
+
+ if (strncasecmp(attribute_data->xffFieldValue[j].field,
+ appIdSession->hsession->xffPrecedence[i], UINT8_MAX) == 0)
+ {
+ char* tmp = strchr(attribute_data->xffFieldValue[j].value, ',');
+ SFIP_RET status;
+
+ if (!tmp)
+ {
+ appIdSession->hsession->xffAddr = sfip_alloc(
+ attribute_data->xffFieldValue[j].value, &status);
+ }
+ // For a comma-separated list of addresses, pick the first address
+ else
+ {
+ attribute_data->xffFieldValue[j].value[tmp -
+ attribute_data->xffFieldValue[j].value] = '\0';
+ appIdSession->hsession->xffAddr = sfip_alloc(
+ attribute_data->xffFieldValue[j].value, &status);
+ }
+ break;
+ }
+ }
+ if (appIdSession->hsession->xffAddr)
+ break;
+ }
+}
+
+static inline void ProcessThirdPartyResults(Packet* p, AppIdData* appIdSession, int
+ confidence, AppId* proto_list, ThirdPartyAppIDAttributeData* attribute_data)
+{
+ int size;
+ AppId serviceAppId = 0;
+ AppId ClientAppId = 0;
+ AppId payloadAppId = 0;
+ AppId referredPayloadAppId = 0;
+ AppIdConfig* pConfig = pAppidActiveConfig;
+
+ if (ThirdPartyAppIDFoundProto(APP_ID_EXCHANGE, proto_list))
+ {
+ if (!appIdSession->payloadAppId)
+ appIdSession->payloadAppId = APP_ID_EXCHANGE;
+ }
+
+ if (ThirdPartyAppIDFoundProto(APP_ID_HTTP, proto_list))
+ {
+ if (app_id_debug_session_flag)
+ LogMessage("AppIdDbg %s flow is HTTP\n", app_id_debug_session);
+ setAppIdFlag(appIdSession, APPID_SESSION_HTTP_SESSION);
+ }
+ if (ThirdPartyAppIDFoundProto(APP_ID_SPDY, proto_list))
+ {
+ if (app_id_debug_session_flag)
+ LogMessage("AppIdDbg %s flow is SPDY\n", app_id_debug_session);
+
+ setAppIdFlag(appIdSession, APPID_SESSION_HTTP_SESSION | APPID_SESSION_SPDY_SESSION);
+ }
+
+ if (getAppIdFlag(appIdSession, APPID_SESSION_HTTP_SESSION))
+ {
+ if (!appIdSession->hsession)
+ {
+ appIdSession->hsession = (httpSession*)snort_calloc(sizeof(httpSession));
+ memset(ptype_scan_counts, 0, 7 * sizeof(ptype_scan_counts[0]));
+ }
+
+ if (getAppIdFlag(appIdSession, APPID_SESSION_SPDY_SESSION))
+ {
+ if (attribute_data->spdyRequesHost)
+ {
+ if (app_id_debug_session_flag)
+ LogMessage("AppIdDbg %s SPDY host is %s\n", app_id_debug_session,
+ attribute_data->spdyRequesHost);
+ if (appIdSession->hsession->host)
+ {
+ snort_free(appIdSession->hsession->host);
+ appIdSession->hsession->chp_finished = 0;
+ }
+ appIdSession->hsession->host = snort_strdup(attribute_data->spdyRequesHost);
+ appIdSession->scan_flags |= SCAN_HTTP_HOST_URL_FLAG;
+ }
+ if (attribute_data->spdyRequestPath)
+ {
+ if (app_id_debug_session_flag)
+ LogMessage("AppIdDbg %s SPDY URI is %s\n", app_id_debug_session,
+ attribute_data->spdyRequestPath);
+ if (appIdSession->hsession->uri)
+ {
+ snort_free(appIdSession->hsession->uri);
+ appIdSession->hsession->chp_finished = 0;
+ }
+ appIdSession->hsession->uri = snort_strdup(attribute_data->spdyRequestPath);
+ }
+ if (attribute_data->spdyRequestScheme &&
+ attribute_data->spdyRequesHost &&
+ attribute_data->spdyRequestPath)
+ {
+ static const char httpsScheme[] = "https";
+ static const char httpScheme[] = "http";
+ const char* scheme;
+
+ if (appIdSession->hsession->url)
+ {
+ snort_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->spdyRequesHost) +
+ strlen(attribute_data->spdyRequestPath) +
+ sizeof("://"); // see sprintf() format
+ appIdSession->hsession->url = (char*)snort_calloc(size);
+ sprintf(appIdSession->hsession->url, "%s://%s%s",
+ scheme, attribute_data->spdyRequesHost, attribute_data->spdyRequestPath);
+ appIdSession->scan_flags |= SCAN_HTTP_HOST_URL_FLAG;
+ }
+ if (attribute_data->spdyRequestScheme)
+ {
+ snort_free(attribute_data->spdyRequestScheme);
+ attribute_data->spdyRequestScheme = nullptr;
+ }
+ if (attribute_data->spdyRequesHost)
+ {
+ snort_free(attribute_data->spdyRequesHost);
+ attribute_data->spdyRequesHost = nullptr;
+ }
+ if (attribute_data->spdyRequestPath)
+ {
+ snort_free(attribute_data->spdyRequestPath);
+ attribute_data->spdyRequestPath = nullptr;
+ }
+ }
+ else
+ {
+ if (attribute_data->httpRequesHost)
+ {
+ if (app_id_debug_session_flag)
+ LogMessage("AppIdDbg %s HTTP host is %s\n", app_id_debug_session,
+ attribute_data->httpRequesHost);
+ if (appIdSession->hsession->host)
+ {
+ snort_free(appIdSession->hsession->host);
+ if (!getAppIdFlag(appIdSession, APPID_SESSION_APP_REINSPECT))
+ appIdSession->hsession->chp_finished = 0;
+ }
+ appIdSession->hsession->host = attribute_data->httpRequesHost;
+ attribute_data->httpRequesHost = nullptr;
+ appIdSession->scan_flags |= SCAN_HTTP_HOST_URL_FLAG;
+ }
+ if (attribute_data->httpRequesUrl)
+ {
+ static const char httpScheme[] = "http://";
+
+ if (appIdSession->hsession->url)
+ {
+ snort_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->httpRequesUrl, httpScheme, sizeof(httpScheme)-1) == 0)
+ {
+ appIdSession->hsession->url =
+ (char*)snort_calloc(strlen(attribute_data->httpRequesUrl) + 2);
+
+ if (appIdSession->hsession->url)
+ sprintf(appIdSession->hsession->url, "https://%s",
+ attribute_data->httpRequesUrl + sizeof(httpScheme)-1);
+
+ snort_free(attribute_data->httpRequesUrl);
+ attribute_data->httpRequesUrl = nullptr;
+ }
+ else
+ {
+ appIdSession->hsession->url = attribute_data->httpRequesUrl;
+ attribute_data->httpRequesUrl = nullptr;
+ }
+
+ appIdSession->scan_flags |= SCAN_HTTP_HOST_URL_FLAG;
+ }
+ if (attribute_data->httpRequestUri)
+ {
+ if (appIdSession->hsession->uri)
+ {
+ snort_free(appIdSession->hsession->uri);
+ if (!getAppIdFlag(appIdSession, APPID_SESSION_APP_REINSPECT))
+ appIdSession->hsession->chp_finished = 0;
+ }
+ appIdSession->hsession->uri = attribute_data->httpRequestUri;
+ appIdSession->hsession->uriOffset = attribute_data->httpRequestUriOffset;
+ appIdSession->hsession->uriEndOffset = attribute_data->httpRequestUriEndOffset;
+ attribute_data->httpRequestUri = nullptr;
+ attribute_data->httpRequestUriOffset = 0;
+ attribute_data->httpRequestUriEndOffset = 0;
+ if (app_id_debug_session_flag)
+ LogMessage("AppIdDbg %s uri (%u-%u) is %s\n", app_id_debug_session,
+ appIdSession->hsession->uriOffset, appIdSession->hsession->uriEndOffset,
+ appIdSession->hsession->uri);
+ }
+ }
+ if (attribute_data->httpRequestVia)
+ {
+ if (appIdSession->hsession->via)
+ {
+ snort_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 = nullptr;
+ appIdSession->scan_flags |= SCAN_HTTP_VIA_FLAG;
+ }
+ else if (attribute_data->httpResponseVia)
+ {
+ if (appIdSession->hsession->via)
+ {
+ snort_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 = nullptr;
+ appIdSession->scan_flags |= SCAN_HTTP_VIA_FLAG;
+ }
+ if (attribute_data->httpRequestUserAgent)
+ {
+ if (appIdSession->hsession->useragent)
+ {
+ snort_free(appIdSession->hsession->useragent);
+ if (!getAppIdFlag(appIdSession, APPID_SESSION_APP_REINSPECT))
+ appIdSession->hsession->chp_finished = 0;
+ }
+ appIdSession->hsession->useragent = attribute_data->httpRequestUserAgent;
+ attribute_data->httpRequestUserAgent = nullptr;
+ 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)
+ LogMessage("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)
+ LogMessage("AppIdDbg %s 3rd party detected and parsed HTTP/2\n",
+ app_id_debug_session);
+ appIdSession->is_http2 = true;
+ }
+ snort_free(attribute_data->httpResponseVersion);
+ attribute_data->httpResponseVersion = nullptr;
+ }
+ if (attribute_data->httpResponseCode)
+ {
+ if (app_id_debug_session_flag)
+ LogMessage("AppIdDbg %s HTTP response code is %s\n", app_id_debug_session,
+ attribute_data->httpResponseCode);
+ if (appIdSession->hsession->response_code)
+ {
+ snort_free(appIdSession->hsession->response_code);
+ if (!getAppIdFlag(appIdSession, APPID_SESSION_APP_REINSPECT))
+ appIdSession->hsession->chp_finished = 0;
+ }
+ appIdSession->hsession->response_code = attribute_data->httpResponseCode;
+ attribute_data->httpResponseCode = nullptr;
+ }
+ // 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)
+ LogMessage("AppIdDbg %s HTTP response upgrade is %s\n", app_id_debug_session,
+ attribute_data->httpResponseUpgrade);
+ if (pAppidActiveConfig->mod_config->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)
+ LogMessage("AppIdDbg %s Got an upgrade to HTTP/2\n",
+ app_id_debug_session);
+ appIdSession->is_http2 = true;
+ }
+ snort_free(attribute_data->httpResponseUpgrade);
+ attribute_data->httpResponseUpgrade = nullptr;
+ }
+ if (!pAppidActiveConfig->mod_config->referred_appId_disabled &&
+ attribute_data->httpRequestReferer)
+ {
+ if (app_id_debug_session_flag)
+ LogMessage("AppIdDbg %s referrer is %s\n", app_id_debug_session,
+ attribute_data->httpRequestReferer);
+ if (appIdSession->hsession->referer)
+ {
+ snort_free(appIdSession->hsession->referer);
+ if (!getAppIdFlag(appIdSession, APPID_SESSION_APP_REINSPECT))
+ appIdSession->hsession->chp_finished = 0;
+ }
+ appIdSession->hsession->referer = attribute_data->httpRequestReferer;
+ attribute_data->httpRequestReferer = nullptr;
+ }
+ if (attribute_data->httpRequestCookie)
+ {
+ if (appIdSession->hsession->cookie)
+ {
+ snort_free(appIdSession->hsession->cookie);
+ if (!getAppIdFlag(appIdSession, APPID_SESSION_APP_REINSPECT))
+ appIdSession->hsession->chp_finished = 0;
+ }
+ appIdSession->hsession->cookie = attribute_data->httpRequestCookie;
+ appIdSession->hsession->cookieOffset = attribute_data->httpRequestCookieOffset;
+ appIdSession->hsession->cookieEndOffset = attribute_data->httpRequestCookieEndOffset;
+ attribute_data->httpRequestCookie = nullptr;
+ attribute_data->httpRequestCookieOffset = 0;
+ attribute_data->httpRequestCookieEndOffset = 0;
+ if (app_id_debug_session_flag)
+ LogMessage("AppIdDbg %s cookie (%u-%u) is %s\n", app_id_debug_session,
+ appIdSession->hsession->cookieOffset, appIdSession->hsession->cookieEndOffset,
+ appIdSession->hsession->cookie);
+ }
+ if (attribute_data->httpResponseContent)
+ {
+ if (appIdSession->hsession->content_type)
+ {
+ snort_free(appIdSession->hsession->content_type);
+ if (!getAppIdFlag(appIdSession, APPID_SESSION_APP_REINSPECT))
+ appIdSession->hsession->chp_finished = 0;
+ }
+ appIdSession->hsession->content_type = attribute_data->httpResponseContent;
+ attribute_data->httpResponseContent = nullptr;
+ appIdSession->scan_flags |= SCAN_HTTP_CONTENT_TYPE_FLAG;
+ }
+ if (ptype_scan_counts[LOCATION_PT] && attribute_data->httpResponseLocation)
+ {
+ if (appIdSession->hsession->location)
+ {
+ snort_free(appIdSession->hsession->location);
+ if (!getAppIdFlag(appIdSession, APPID_SESSION_APP_REINSPECT))
+ appIdSession->hsession->chp_finished = 0;
+ }
+ appIdSession->hsession->location = attribute_data->httpResponseLocation;
+ attribute_data->httpResponseLocation = nullptr;
+ }
+ if (attribute_data->httpRequestBody)
+ {
+ if (app_id_debug_session_flag)
+ LogMessage("AppIdDbg %s got a request body %s\n", app_id_debug_session,
+ attribute_data->httpRequestBody);
+ if (appIdSession->hsession->req_body)
+ {
+ snort_free(appIdSession->hsession->req_body);
+ if (!getAppIdFlag(appIdSession, APPID_SESSION_APP_REINSPECT))
+ appIdSession->hsession->chp_finished = 0;
+ }
+ appIdSession->hsession->req_body = attribute_data->httpRequestBody;
+ attribute_data->httpRequestBody = nullptr;
+ }
+ if (ptype_scan_counts[BODY_PT] && attribute_data->httpResponseBody)
+ {
+ if (appIdSession->hsession->body)
+ {
+ snort_free(appIdSession->hsession->body);
+ if (!getAppIdFlag(appIdSession, APPID_SESSION_APP_REINSPECT))
+ appIdSession->hsession->chp_finished = 0;
+ }
+ appIdSession->hsession->body = attribute_data->httpResponseBody;
+ attribute_data->httpResponseBody = nullptr;
+ }
+ 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)
+ snort_free(appIdSession->hsession->server);
+ appIdSession->hsession->server = attribute_data->httpResponseServer;
+ attribute_data->httpResponseServer = nullptr;
+ appIdSession->scan_flags |= SCAN_HTTP_VENDOR_FLAG;
+ }
+ if (attribute_data->httpRequestXWorkingWith)
+ {
+ if (appIdSession->hsession->x_working_with)
+ snort_free(appIdSession->hsession->x_working_with);
+ appIdSession->hsession->x_working_with = attribute_data->httpRequestXWorkingWith;
+ attribute_data->httpRequestXWorkingWith = nullptr;
+ appIdSession->scan_flags |= SCAN_HTTP_XWORKINGWITH_FLAG;
+ }
+ }
+ else if (ThirdPartyAppIDFoundProto(APP_ID_RTMP, proto_list) ||
+ ThirdPartyAppIDFoundProto(APP_ID_RTSP, proto_list))
+ {
+ if (!appIdSession->hsession)
+ appIdSession->hsession = (httpSession*)snort_calloc(sizeof(httpSession));
+
+ if (!appIdSession->hsession->url)
+ {
+ if (attribute_data->httpRequesUrl)
+ {
+ appIdSession->hsession->url = attribute_data->httpRequesUrl;
+ attribute_data->httpRequesUrl = nullptr;
+ appIdSession->scan_flags |= SCAN_HTTP_HOST_URL_FLAG;
+ }
+ }
+
+ if (!pAppidActiveConfig->mod_config->referred_appId_disabled &&
+ !appIdSession->hsession->referer)
+ {
+ if (attribute_data->httpRequestReferer)
+ {
+ appIdSession->hsession->referer = attribute_data->httpRequestReferer;
+ attribute_data->httpRequestReferer = nullptr;
+ }
+ }
+
+ if (appIdSession->hsession->url || (confidence == 100 &&
+ appIdSession->session_packet_count > pAppidActiveConfig->mod_config->rtmp_max_packets))
+ {
+ if (appIdSession->hsession->url)
+ {
+ if (((getAppIdFromUrl(nullptr, appIdSession->hsession->url, nullptr,
+ appIdSession->hsession->referer, &ClientAppId, &serviceAppId,
+ &payloadAppId, &referredPayloadAppId, 1, &pConfig->detectorHttpConfig)) ||
+ (getAppIdFromUrl(nullptr, appIdSession->hsession->url, nullptr,
+ 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(appIdSession, ClientAppId, nullptr);
+ if (appIdSession->serviceAppId <= APP_ID_NONE)
+ seServiceAppIdData(appIdSession, serviceAppId, nullptr, nullptr);
+
+ // DO overwrite a previously-set data
+ setPayloadAppIdData(appIdSession, (ApplicationId)payloadAppId, nullptr);
+ 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);
+ }
+ appIdSession->tpsession = nullptr;
+ clearAppIdFlag(appIdSession, APPID_SESSION_APP_REINSPECT);
+ }
+ }
+ else if (ThirdPartyAppIDFoundProto(APP_ID_SSL, proto_list))
+ {
+ AppId tmpAppId = APP_ID_NONE;
+
+ if (thirdparty_appid_module && appIdSession->tpsession)
+ tmpAppId = thirdparty_appid_module->session_appid_get(appIdSession->tpsession);
+
+ setAppIdFlag(appIdSession, APPID_SESSION_SSL_SESSION);
+
+ if (!appIdSession->tsession)
+ appIdSession->tsession = (tlsSession*)snort_calloc(sizeof(tlsSession));
+
+ if (!appIdSession->ClientAppId)
+ setClientAppIdData(appIdSession, APP_ID_SSL_CLIENT, nullptr);
+
+ if (attribute_data->tlsHost)
+ {
+ if (appIdSession->tsession->tls_host)
+ snort_free(appIdSession->tsession->tls_host);
+ appIdSession->tsession->tls_host = attribute_data->tlsHost;
+ attribute_data->tlsHost = nullptr;
+ if (testSSLAppIdForReinspect(tmpAppId))
+ appIdSession->scan_flags |= SCAN_SSL_HOST_FLAG;
+ }
+ if (testSSLAppIdForReinspect(tmpAppId))
+ {
+ if (attribute_data->tlsCname)
+ {
+ if (appIdSession->tsession->tls_cname)
+ snort_free(appIdSession->tsession->tls_cname);
+ appIdSession->tsession->tls_cname = attribute_data->tlsCname;
+ attribute_data->tlsCname = nullptr;
+ }
+ if (attribute_data->tlsOrgUnit)
+ {
+ if (appIdSession->tsession->tls_orgUnit)
+ snort_free(appIdSession->tsession->tls_orgUnit);
+ appIdSession->tsession->tls_orgUnit = attribute_data->tlsOrgUnit;
+ attribute_data->tlsOrgUnit = nullptr;
+ }
+ }
+ }
+ else if (ThirdPartyAppIDFoundProto(APP_ID_FTP_CONTROL, proto_list))
+ {
+ if (!pAppidActiveConfig->mod_config->ftp_userid_disabled && attribute_data->ftpCommandUser)
+ {
+ if (appIdSession->username)
+ snort_free(appIdSession->username);
+ appIdSession->username = attribute_data->ftpCommandUser;
+ attribute_data->ftpCommandUser = nullptr;
+ appIdSession->usernameService = APP_ID_FTP_CONTROL;
+ setAppIdFlag(appIdSession, APPID_SESSION_LOGIN_SUCCEEDED);
+ }
+ }
+}
+
+void appSetServiceValidator(RNAServiceValidationFCN fcn, AppId appId, unsigned extractsInfo,
+ AppIdConfig* pConfig)
+{
+ AppInfoTableEntry* pEntry = appInfoEntryGet(appId, pConfig);
+ if (!pEntry)
+ {
+ ErrorMessage("AppId: invalid direct service AppId, %d, for %p", appId, (void*)fcn);
+ return;
+ }
+ extractsInfo &= (APPINFO_FLAG_SERVICE_ADDITIONAL | APPINFO_FLAG_SERVICE_UDP_REVERSED);
+ if (!extractsInfo)
+ {
+ DebugFormat(DEBUG_APPID, "Ignoring direct service without info for %p with AppId %d",
+ (void*)fcn,
+ appId);
+ return;
+ }
+ pEntry->svrValidator = ServiceGetServiceElement(fcn, nullptr, pConfig);
+ if (pEntry->svrValidator)
+ pEntry->flags |= extractsInfo;
+ else
+ ErrorMessage("AppId: failed to find a service element for %p with AppId %d", (void*)fcn,
+ appId);
+}
+
+void appSetLuaServiceValidator(RNAServiceValidationFCN fcn, AppId appId, unsigned extractsInfo,
+ struct Detector* data)
+{
+ AppInfoTableEntry* entry;
+ AppIdConfig* pConfig = pAppidActiveConfig;
+
+ if ((entry = appInfoEntryGet(appId, pConfig)))
+ {
+ entry->flags |= APPINFO_FLAG_ACTIVE;
+
+ extractsInfo &= (APPINFO_FLAG_SERVICE_ADDITIONAL | APPINFO_FLAG_SERVICE_UDP_REVERSED);
+ if (!extractsInfo)
+ {
+ DebugFormat(DEBUG_LOG,
+ "Ignoring direct service without info for %p %p with AppId %d\n",
+ (void*)fcn, (void*)data, appId);
+ return;
+ }
+
+ entry->svrValidator = ServiceGetServiceElement(fcn, data, pConfig);
+ if (entry->svrValidator)
+ entry->flags |= extractsInfo;
+ else
+ ErrorMessage("AppId: Failed to find a service element for %p %p with AppId %d",
+ (void*)fcn, (void*)data, appId);
+ }
+ else
+ {
+ ErrorMessage("Invalid direct service AppId, %d, for %p %p\n",
+ appId, (void*)fcn, (void*)data);
+ }
+}
+
+void appSetClientValidator(RNAClientAppFCN fcn, AppId appId, unsigned extractsInfo,
+ AppIdConfig* pConfig)
+{
+ AppInfoTableEntry* pEntry = appInfoEntryGet(appId, pConfig);
+ if (!pEntry)
+ {
+ ErrorMessage("AppId: invalid direct client application AppId, %d, for %p\n",
+ appId, (void*)fcn);
+ return;
+ }
+ extractsInfo &= (APPINFO_FLAG_CLIENT_ADDITIONAL | APPINFO_FLAG_CLIENT_USER);
+ if (!extractsInfo)
+ {
+ DebugFormat(DEBUG_LOG,
+ "Ignoring direct client application without info for %p with AppId %d",
+ (void*)fcn, appId);
+ return;
+ }
+ pEntry->clntValidator = ClientAppGetClientAppModule(fcn, nullptr, &pConfig->clientAppConfig);
+ if (pEntry->clntValidator)
+ pEntry->flags |= extractsInfo;
+ else
+ ErrorMessage("Appid: Failed to find a client application module for %p with AppId %d\n",
+ (void*)fcn, appId);
+}
+
+void appSetLuaClientValidator(RNAClientAppFCN fcn, AppId appId, unsigned extractsInfo, struct
+ Detector* data)
+{
+ AppInfoTableEntry* entry;
+ AppIdConfig* pConfig = pAppidActiveConfig;
+
+ if ((entry = appInfoEntryGet(appId, pConfig)))
+ {
+ entry->flags |= APPINFO_FLAG_ACTIVE;
+ extractsInfo &= (APPINFO_FLAG_CLIENT_ADDITIONAL | APPINFO_FLAG_CLIENT_USER);
+ if (!extractsInfo)
+ {
+ DebugFormat(DEBUG_LOG,
+ "Ignoring direct client application without info for %p %p with AppId %d\n",
+ (void*)fcn, (void*)data, appId);
+ return;
+ }
+
+ entry->clntValidator = ClientAppGetClientAppModule(fcn, data, &pConfig->clientAppConfig);
+ if (entry->clntValidator)
+ entry->flags |= extractsInfo;
+ else
+ ErrorMessage(
+ "AppId: Failed to find a client application module for %p %p with AppId %d",
+ (void*)fcn, (void*)data, appId);
+ }
+ else
+ {
+ ErrorMessage("Invalid direct client application AppId, %d, for %p %p\n",
+ appId, (void*)fcn, (void*)data);
+ return;
+ }
+}
+
+void AppIdAddUser(AppIdData* flowp, const char* username, AppId appId, int success)
+{
+ if (flowp->username)
+ snort_free(flowp->username);
+ flowp->username = snort_strdup(username);
+ flowp->usernameService = appId;
+ if (success)
+ setAppIdFlag(flowp, APPID_SESSION_LOGIN_SUCCEEDED);
+ else
+ clearAppIdFlag(flowp, APPID_SESSION_LOGIN_SUCCEEDED);
+}
+
+void AppIdAddDnsQueryInfo(AppIdData* flow,
+ uint16_t id,
+ const uint8_t* host, uint8_t host_len, uint16_t host_offset,
+ uint16_t record_type)
+{
+ if ( flow->dsession )
+ {
+ if ( ( flow->dsession->state != 0 ) && ( flow->dsession->id != id ) )
+ AppIdResetDnsInfo(flow);
+ }
+ else
+ flow->dsession = (dnsSession*)snort_calloc(sizeof(dnsSession));
+
+ if (flow->dsession->state & DNS_GOT_QUERY)
+ return;
+ flow->dsession->state |= DNS_GOT_QUERY;
+
+ flow->dsession->id = id;
+ flow->dsession->record_type = record_type;
+
+ if (!flow->dsession->host)
+ {
+ if ((host != nullptr) && (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 AppIdAddDnsResponseInfo(AppIdData* 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->state != 0 ) && ( flow->dsession->id != id ) )
+ AppIdResetDnsInfo(flow);
+ }
+ else
+ flow->dsession = (dnsSession*)snort_calloc(sizeof(*flow->dsession));
+
+ 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 != nullptr) && (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(AppIdData* flow)
+{
+ if (flow->dsession)
+ {
+ snort_free(flow->dsession->host);
+ memset(flow->dsession, 0, sizeof(*(flow->dsession)));
+ }
+}
+
+void AppIdAddPayload(AppIdData* flow, AppId payload_id)
+{
+ if (pAppidActiveConfig->mod_config->instance_id)
+ checkSandboxDetection(payload_id);
+ flow->payloadAppId = payload_id;
+}
+
+AppId getOpenAppId(void* ssnptr)
+{
+ AppIdData* session;
+ AppId 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,
+// AppId* serviceAppId, AppId* ClientAppId, AppId* payloadAppId)
+int sslAppGroupIdLookup(void*, const char*, const char*, AppId*, AppId*, AppId*)
+{
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ AppIdData* 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;
+ }
+#endif
+
+ return 0;
+}
+
+void httpHeaderCallback(Packet* p, HttpParsedHeaders* const headers)
+{
+ AppIdData* session;
+ int direction;
+ AppIdConfig* pConfig = pAppidActiveConfig;
+
+ if (thirdparty_appid_module)
+ return;
+ if (!p || !(session = getAppIdData(p->flow)))
+ return;
+
+ direction = p->is_from_client() ? APP_ID_FROM_INITIATOR : APP_ID_FROM_RESPONDER;
+
+ if (!session->hsession)
+ session->hsession = (decltype(session->hsession))snort_calloc(sizeof(httpSession));
+
+ if (direction == APP_ID_FROM_INITIATOR)
+ {
+ if (headers->host.start)
+ {
+ snort_free(session->hsession->host);
+ session->hsession->host = strndup((char*)headers->host.start, headers->host.len);
+ session->scan_flags |= SCAN_HTTP_HOST_URL_FLAG;
+
+ if (headers->url.start)
+ {
+ snort_free(session->hsession->url);
+ session->hsession->url = (char*)snort_calloc(sizeof(HTTP_PREFIX) +
+ headers->host.len + headers->url.len);
+ 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;
+ }
+ }
+ if (headers->userAgent.start)
+ {
+ snort_free(session->hsession->useragent);
+ session->hsession->useragent = strndup((char*)headers->userAgent.start,
+ headers->userAgent.len);
+ session->scan_flags |= SCAN_HTTP_USER_AGENT_FLAG;
+ }
+ if (headers->referer.start)
+ {
+ snort_free(session->hsession->referer);
+ session->hsession->referer = strndup((char*)headers->referer.start,
+ headers->referer.len);
+ }
+ if (headers->via.start)
+ {
+ snort_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)
+ {
+ snort_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)
+ {
+ snort_free(session->hsession->content_type);
+ session->hsession->content_type = strndup((char*)headers->contentType.start,
+ headers->contentType.len);
+ }
+ if (headers->responseCode.start)
+ {
+ long responseCodeNum;
+ responseCodeNum = strtoul((char*)headers->responseCode.start, nullptr, 10);
+ if (responseCodeNum > 0 && responseCodeNum < 700)
+ {
+ snort_free(session->hsession->response_code);
+ session->hsession->response_code = strndup((char*)headers->responseCode.start,
+ headers->responseCode.len);
+ }
+ }
+ }
+ processHTTPPacket(p, session, direction, headers, pConfig);
+
+ setAppIdFlag(session, APPID_SESSION_SERVICE_DETECTED | APPID_SESSION_HTTP_SESSION);
+
+// FIXIT-M:
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ _dpd.streamAPI->set_application_id(p->flow, pickServiceAppId(session),
+ pickClientAppId(session), pickPayloadId(session), pickMiscAppId(session));
+#endif
+}
+
+static inline void ExamineRtmpMetadata(AppIdData* appIdSession)
+{
+ AppId serviceAppId = 0;
+ AppId ClientAppId = 0;
+ AppId payloadAppId = 0;
+ AppId referredPayloadAppId = 0;
+ char* version = nullptr;
+ httpSession* hsession;
+ AppIdConfig* pConfig = pAppidActiveConfig;
+
+ if (!appIdSession->hsession)
+ appIdSession->hsession = (httpSession*)snort_calloc(sizeof(httpSession));
+
+ hsession = appIdSession->hsession;
+
+ if (hsession->url)
+ {
+ if (((getAppIdFromUrl(nullptr, hsession->url, &version,
+ hsession->referer, &ClientAppId, &serviceAppId,
+ &payloadAppId, &referredPayloadAppId, 1, &pConfig->detectorHttpConfig)) ||
+ (getAppIdFromUrl(nullptr, 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(appIdSession, ClientAppId, nullptr);
+ if (appIdSession->serviceAppId <= APP_ID_NONE)
+ seServiceAppIdData(appIdSession, serviceAppId, nullptr, nullptr);
+
+ /* DO overwrite a previously-set data */
+ setPayloadAppIdData(appIdSession, (ApplicationId)payloadAppId, nullptr);
+ setReferredPayloadAppIdData(appIdSession, referredPayloadAppId);
+ }
+ }
+}
+
+void checkSandboxDetection(AppId appId)
+{
+ AppInfoTableEntry* entry;
+ AppIdConfig* pConfig = pAppidActiveConfig;
+
+ if (pAppidActiveConfig->mod_config->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);
+ }
+ }
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// fw_appid.h author Sourcefire Inc.
+
+#ifndef FW_APPID_H
+#define FW_APPID_H
+
+#include "client_plugins/client_app_api.h"
+#include "util/common_util.h"
+
+#include "appid.h"
+#include "appid_api.h"
+#include "appid_config.h"
+#include "appid_flow_data.h"
+#include "application_ids.h"
+#include "app_info_table.h"
+#include "service_plugins/service_api.h"
+#include "thirdparty_appid_utils.h"
+
+#define PP_APP_ID 1
+
+#define MIN_SFTP_PACKET_COUNT 30
+#define MAX_SFTP_PACKET_COUNT 55
+
+struct HttpParsedHeaders;
+struct Packet;
+class AppIdConfig;
+
+extern uint8_t appIdPriorityArray[SF_APPID_MAX+1];
+
+enum ServiceEventType {};
+
+AppIdData* getAppIdData(void* lwssn);
+
+void fwAppIdInit();
+void fwAppIdFini(AppIdConfig*);
+void fwAppIdSearch(Packet*);
+void httpHeaderCallback(Packet*, HttpParsedHeaders* const);
+void SipSessionSnortCallback(void* ssnptr, ServiceEventType, void* eventData);
+
+void readRnaAppMappingTable(const char* path, AppIdConfig*);
+AppId appGetAppFromServiceId(uint32_t serviceId, AppIdConfig*);
+AppId appGetAppFromClientId(uint32_t clientId, AppIdConfig*);
+AppId appGetAppFromPayloadId(uint32_t payloadId, AppIdConfig*);
+void appSharedDataDelete(AppIdData*);
+void AppIdAddUser(AppIdData*, const char* username, AppId, int success);
+void AppIdAddDnsQueryInfo(
+ AppIdData*,
+ uint16_t id,
+ const uint8_t* host, uint8_t host_len, uint16_t host_offset,
+ uint16_t record_type
+);
+
+void AppIdAddDnsResponseInfo(
+ AppIdData*,
+ uint16_t id,
+ const uint8_t* host, uint8_t host_len, uint16_t host_offset,
+ uint8_t response_type, uint32_t ttl
+);
+
+void AppIdResetDnsInfo(AppIdData*);
+
+void AppIdAddPayload(AppIdData*, AppId);
+AppIdData* appSharedDataAlloc(IpProtocol proto, const sfip_t*);
+AppId getOpenAppId(void* ssnptr);
+
+void appSetServiceValidator(
+ RNAServiceValidationFCN, AppId, unsigned extractsInfo, AppIdConfig*);
+
+void appSetLuaServiceValidator(
+ RNAServiceValidationFCN, AppId, unsigned extractsInfo, Detector* dat);
+
+void appSetClientValidator(
+ RNAClientAppFCN, AppId, unsigned extractsInfo, AppIdConfig*);
+
+void appSetLuaClientValidator(
+ RNAClientAppFCN, AppId, unsigned extractsInfo, Detector* data);
+
+int sslAppGroupIdLookup(
+ void* ssnptr,
+ const char* serverName,
+ const char* commonName,
+ AppId* serviceAppId,
+ AppId* ClientAppId,
+ AppId* payloadAppId
+);
+
+AppId getAppId(void* ssnptr);
+
+#ifdef FW_TRACKER_DEBUG
+void logAppIdInfo(SFSnortPacket* p, char* message, AppId 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;
+
+extern ProfileStats httpPerfStats;
+extern ProfileStats clientMatchPerfStats;
+extern ProfileStats serviceMatchPerfStats;
+extern ProfileStats luaDetectorsPerfStats;
+extern ProfileStats tpPerfStats;
+extern ProfileStats tpLibPerfStats;
+
+extern unsigned dhcp_fp_table_size;
+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 int app_id_debug;
+extern unsigned isIPv4HostMonitored(uint32_t ip4, int32_t zone);
+extern void checkSandboxDetection(AppId appId);
+
+inline void initializePriorityArray()
+{
+ for ( int i=0; i < SF_APPID_MAX; ++i )
+ appIdPriorityArray[i] = 2;
+}
+
+inline void setAppPriority(AppId app_id, uint8_t bit_val)
+{
+ if ( app_id < SF_APPID_MAX && bit_val <= APPID_MAX_PRIORITY )
+ appIdPriorityArray[app_id] = bit_val;
+}
+
+inline int getAppPriority(AppId app_id)
+{
+ if (app_id > APP_ID_NONE && app_id < SF_APPID_MAX)
+ return appIdPriorityArray[app_id];
+
+ return -1;
+}
+
+inline int ThirdPartyAppIDFoundProto(AppId proto, AppId* 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
+}
+
+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;
+}
+
+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;
+}
+
+inline AppId isAppDetectionDone(AppIdData* flow)
+{ return getAppIdFlag(flow, APPID_SESSION_SERVICE_DETECTED); }
+
+inline AppId pickServiceAppId(AppIdData* flow)
+{
+ AppId 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, pAppidActiveConfig)
+ || appInfoEntryFlagGet(flow->tpAppId, APPINFO_FLAG_DEFER, pAppidActiveConfig);
+
+ 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;
+}
+
+inline AppId pickOnlyServiceAppId(AppIdData* 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, pAppidActiveConfig)
+ || appInfoEntryFlagGet(flow->tpAppId, APPINFO_FLAG_DEFER, pAppidActiveConfig);
+
+ 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;
+}
+
+inline AppId pickMiscAppId(AppIdData* 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;
+}
+
+inline AppId pickClientAppId(AppIdData* 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;
+}
+
+inline AppId pickPayloadId(AppIdData* 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, pAppidActiveConfig))
+ return flow->tpPayloadAppId;
+ else if (flow->payloadAppId > APP_ID_NONE)
+ return flow->payloadAppId;
+ else if (flow->tpPayloadAppId > APP_ID_NONE)
+ return flow->tpPayloadAppId;
+
+ return APP_ID_NONE;
+}
+
+inline AppId pickReferredPayloadId(AppIdData* 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;
+}
+
+inline AppId fwPickServiceAppId(AppIdData* session)
+{
+ AppId appId;
+ appId = pickServiceAppId(session);
+ if (appId == APP_ID_NONE)
+ appId = session->encrypted.serviceAppId;
+ return appId;
+}
+
+inline AppId fwPickMiscAppId(AppIdData* session)
+{
+ AppId appId;
+ appId = pickMiscAppId(session);
+ if (appId == APP_ID_NONE)
+ appId = session->encrypted.miscAppId;
+ return appId;
+}
+
+inline AppId fwPickClientAppId(AppIdData* session)
+{
+ AppId appId;
+ appId = pickClientAppId(session);
+ return appId;
+}
+
+inline AppId fwPickPayloadAppId(AppIdData* session)
+{
+ AppId appId;
+ appId = pickPayloadId(session);
+ if (appId == APP_ID_NONE)
+ appId = session->encrypted.payloadAppId;
+ return appId;
+}
+
+inline AppId fwPickReferredPayloadAppId(AppIdData* session)
+{
+ AppId appId;
+ appId = pickReferredPayloadId(session);
+ if (appId == APP_ID_NONE)
+ appId = session->encrypted.referredAppId;
+ return appId;
+}
+
+inline AppIdData* appSharedGetData(const Packet* p)
+{
+ if ( p && p->flow )
+ return (AppIdData*)p->flow->get_application_data(AppIdData::flow_id);
+
+ return nullptr;
+}
+
+inline unsigned int isFwSessionSslDecrypted(AppIdData* session)
+{
+ return getAppIdFlag(session, APPID_SESSION_DECRYPTED);
+}
+
+inline int testSSLAppIdForReinspect(AppId app_id)
+{
+ if (app_id <= SF_APPID_MAX &&
+ (app_id == APP_ID_SSL ||
+ appInfoEntryFlagGet(app_id, APPINFO_FLAG_SSL_INSPECT, pAppidActiveConfig)))
+ return 1;
+ else
+ return 0;
+}
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// host_port_app_cache.cc author Sourcefire Inc.
+
+#include "host_port_app_cache.h"
+
+#include "appid_config.h"
+#include "hash/sfxhash.h"
+#include "log/messages.h"
+#include "sfip/sf_ip.h"
+
+void hostPortAppCacheInit(AppIdConfig* pConfig)
+{
+ auto hash = sfxhash_new(
+ 2048, sizeof(HostPortKey), sizeof(HostPortVal), 0, 0, nullptr, nullptr, 0);
+
+ if ( hash )
+ pConfig->hostPortCache = hash;
+
+ else
+ ErrorMessage("failed to allocate HostPort map");
+}
+
+void hostPortAppCacheFini(AppIdConfig* pConfig)
+{
+ if ( pConfig->hostPortCache )
+ {
+ sfxhash_delete(pConfig->hostPortCache);
+ pConfig->hostPortCache = nullptr;
+ }
+}
+
+HostPortVal* hostPortAppCacheFind(
+ const sfip_t* snort_ip, uint16_t port, IpProtocol protocol, const AppIdConfig* pConfig)
+{
+ HostPortKey hk;
+ sfip_set_ip(&hk.ip, snort_ip);
+ hk.port = port;
+ hk.proto = protocol;
+
+ return (HostPortVal*)sfxhash_find(pConfig->hostPortCache, &hk);
+}
+
+int hostPortAppCacheAdd(const in6_addr* ip, uint16_t port, IpProtocol proto, unsigned type,
+ AppId appId, AppIdConfig* pConfig)
+{
+ HostPortKey hk;
+ HostPortVal hv;
+ memcpy(&hk.ip, ip, sizeof(hk.ip));
+ hk.port = port;
+ hk.proto = proto;
+ hv.appId = appId;
+ hv.type = type;
+
+ return sfxhash_add(pConfig->hostPortCache, &hk, &hv) ? 0 : 1;
+}
+
+void hostPortAppCacheDump(const AppIdConfig* pConfig)
+{
+ for ( SFXHASH_NODE* node = sfxhash_findfirst(pConfig->hostPortCache);
+ node;
+ node = sfxhash_findnext(pConfig->hostPortCache))
+ {
+ char inet_buffer[INET6_ADDRSTRLEN];
+ HostPortKey* hk;
+ HostPortVal* hv;
+
+ hk = (HostPortKey*)node->key;
+ hv = (HostPortVal*)node->data;
+
+ inet_ntop(AF_INET6, &hk->ip, inet_buffer, sizeof(inet_buffer));
+ printf("\tip=%s, \tport %d, \tip_proto %d, \ttype=%u, \tappId=%d\n", inet_buffer, hk->port,
+ to_utype(hk->proto), hv->type, hv->appId);
+ }
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// host_port_app_cache.h author Sourcefire Inc.
+
+#ifndef HOST_PORT_APP_CACHE_H
+#define HOST_PORT_APP_CACHE_H
+
+#include "sfip/sfip_t.h"
+#include "appid_api.h"
+
+class AppIdConfig;
+enum class IpProtocol : uint8_t;
+
+struct HostPortKey
+{
+ sfip_t ip;
+ uint16_t port;
+ IpProtocol proto;
+};
+
+struct HostPortVal
+{
+ AppId appId;
+ unsigned type;
+};
+
+void hostPortAppCacheInit(AppIdConfig*);
+void hostPortAppCacheFini(AppIdConfig*);
+// FIXIT-M: Should proto be IpProtocol or ProtocolId?
+HostPortVal* hostPortAppCacheFind(
+ const sfip_t*, uint16_t port, IpProtocol proto, const AppIdConfig*);
+
+int hostPortAppCacheAdd(
+ const in6_addr*, uint16_t port, IpProtocol proto, unsigned type, AppId, AppIdConfig*);
+void hostPortAppCacheDump(const AppIdConfig*);
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// http_common.h author Sourcefire Inc.
+
+#ifndef HTTP_COMMON_H
+#define HTTP_COMMON_H
+
+#include "appid.h"
+#include "appid_api.h"
+#include "utils/sflsq.h"
+
+// FIXIT-H rename util/ so we don't confuse it with src/utils
+#include "util/sf_multi_mpse.h"
+
+#define MAX_USERNAME_SIZE 64
+#define MAX_URL_SIZE 65535
+
+class SearchTool;
+struct tMlmpTree;
+
+// These values are used in Lua code as raw numbers. Do NOT reassign new values.
+enum DHPSequence
+{
+ SINGLE = 0,
+ SKYPE_URL = 1,
+ SKYPE_VERSION = 2,
+ BT_ANNOUNCE = 3,
+ BT_OTHER = 4,
+ USER_AGENT_HEADER = 5
+};
+
+struct DetectorHTTPPattern
+{
+ DHPSequence seq;
+ AppId service_id;
+ AppId client_app;
+ AppId payload;
+ int pattern_size;
+ uint8_t* pattern;
+ AppId appId;
+};
+
+struct HTTPListElement
+{
+ DetectorHTTPPattern detectorHTTPPattern;
+ HTTPListElement* next;
+};
+
+#define APPL_VERSION_LENGTH 40
+
+struct UrlUserData
+{
+ uint32_t service_id;
+ uint32_t client_app;
+ uint32_t payload;
+ AppId appId;
+ tMlpPattern query;
+};
+
+struct DetectorAppUrlPattern
+{
+ struct
+ {
+ tMlpPattern host;
+ tMlpPattern path;
+ tMlpPattern scheme;
+ } patterns;
+
+ UrlUserData userData;
+};
+
+struct DetectorAppUrlList
+{
+ DetectorAppUrlPattern** urlPattern;
+ size_t usedCount;
+ size_t allocatedCount;
+};
+
+// 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.
+enum ActionType
+{
+ 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
+ MAX_ACTION_TYPE = SEARCH_UNSUPPORTED,
+};
+
+// These values are used in Lua code as raw numbers. Do NOT reassign new values.
+enum PatternType
+{
+ // 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,
+};
+
+#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))
+
+struct CHPApp
+{
+ AppId 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];
+ int ptype_rewrite_insert_used[NUMBER_OF_PTYPES]; // boolean
+};
+
+struct CHPAction
+{
+ AppId appIdInstance; // * see note above
+ unsigned precedence; // order of creation
+ int key_pattern;
+ PatternType ptype;
+ int psize;
+ char* pattern;
+ ActionType action;
+ char* action_data;
+ CHPApp* chpapp;
+};
+
+struct CHPListElement
+{
+ CHPAction chp_action;
+ CHPListElement* next;
+};
+
+struct HttpPatternLists
+{
+ HTTPListElement* hostPayloadPatternList;
+ HTTPListElement* urlPatternList;
+ HTTPListElement* clientAgentPatternList;
+ HTTPListElement* contentTypePatternList;
+ CHPListElement* chpList;
+ DetectorAppUrlList appUrlList;
+ DetectorAppUrlList RTMPUrlList;
+};
+
+// url parts extracted from http headers.
+struct UrlStruct
+{
+ tMlpPattern host; /*from host header */
+ tMlpPattern path; /*from GET/POST request */
+ tMlpPattern scheme; /*hardcoded to "http:" */
+ tMlpPattern query; /*query match for version number */
+};
+
+struct HosUrlDetectorPattern
+{
+ tMlpPattern host;
+ tMlpPattern path;
+ tMlpPattern query;
+ uint32_t payload_id;
+ uint32_t service_id;
+ uint32_t client_id;
+ AppId appId;
+ DHPSequence seq;
+ HosUrlDetectorPattern* next;
+};
+
+struct HosUrlPatternsList
+{
+ HosUrlDetectorPattern* head;
+ HosUrlDetectorPattern* tail;
+};
+
+struct DetectorHttpConfig
+{
+ SearchTool* url_matcher;
+ SearchTool* client_agent_matcher;
+ SearchTool* via_matcher;
+ tMlmpTree* hosUrlMatcher;
+ tMlmpTree* RTMPHosUrlMatcher;
+ SearchTool* header_matcher;
+ SearchTool* content_type_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.
+ SearchTool* chp_user_agent_matcher;
+ SearchTool* chp_host_matcher;
+ SearchTool* chp_referer_matcher;
+ SearchTool* chp_uri_matcher;
+ SearchTool* chp_cookie_matcher;
+ SearchTool* chp_content_type_matcher;
+ SearchTool* chp_location_matcher;
+ SearchTool* chp_body_matcher;
+ // TODO: chp_req_body_matcher is not being used anywhere in the code, should it be removed?
+ SearchTool* chp_req_body_matcher;
+
+ SearchTool* chp_matchers[MAX_PATTERN_TYPE+1];
+
+ HosUrlPatternsList* hosUrlPatternsList;
+};
+
+extern AppId getAppIdByHttpUrl(UrlStruct* url, UrlUserData** rnaData);
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// length_app_cache.cc author Sourcefire Inc.
+
+#include "length_app_cache.h"
+
+#include "hash/sfxhash.h"
+#include "log/messages.h"
+
+#include "appid_config.h"
+#include "application_ids.h"
+
+#define HASH_NUM_ROWS (1024)
+
+void lengthAppCacheInit(AppIdConfig* pConfig)
+{
+ if (!(pConfig->lengthCache = sfxhash_new(HASH_NUM_ROWS,
+ sizeof(LengthKey),
+ sizeof(AppId),
+ 0,
+ 0,
+ nullptr,
+ nullptr,
+ 0)))
+ {
+ ErrorMessage("lengthAppCache: Failed to allocate length cache!");
+ }
+}
+
+void lengthAppCacheFini(AppIdConfig* pConfig)
+{
+ if (pConfig->lengthCache)
+ {
+ sfxhash_delete(pConfig->lengthCache);
+ pConfig->lengthCache = nullptr;
+ }
+}
+
+AppId lengthAppCacheFind(const LengthKey* key, const AppIdConfig* pConfig)
+{
+ AppId* val;
+
+ val = (AppId*)sfxhash_find(pConfig->lengthCache, (void*)key);
+ if (val == nullptr)
+ {
+ return APP_ID_NONE; /* no match */
+ }
+ else
+ {
+ return *val; /* match found */
+ }
+}
+
+bool lengthAppCacheAdd(const LengthKey* key, AppId val, AppIdConfig* pConfig)
+{
+ if (sfxhash_add(pConfig->lengthCache, (void*)key, (void*)&val))
+ {
+ return false;
+ }
+ return true;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// length_app_cache.h author Sourcefire Inc.
+
+#ifndef LENGTH_APP_CACHE_H
+#define LENGTH_APP_CACHE_H
+
+#include "appid_api.h"
+
+#define LENGTH_SEQUENCE_CNT_MAX (5)
+
+#pragma pack(1)
+
+class AppIdConfig;
+enum class IpProtocol : uint8_t;
+
+struct LengthSequenceEntry
+{
+ uint8_t direction; /* APP_ID_FROM_INITIATOR or APP_ID_FROM_RESPONDER */
+ uint16_t length; /* payload size (bytes) */
+};
+
+struct LengthKey
+{
+ IpProtocol proto; /* IpProtocol::TCP or IpProtocol::UDP */
+ uint8_t sequence_cnt; /* num valid entries in sequence */
+ LengthSequenceEntry sequence[LENGTH_SEQUENCE_CNT_MAX];
+};
+
+#pragma pack()
+
+void lengthAppCacheInit(AppIdConfig*);
+void lengthAppCacheFini(AppIdConfig*);
+AppId lengthAppCacheFind(const LengthKey*, const AppIdConfig*);
+bool lengthAppCacheAdd(const LengthKey*, AppId, AppIdConfig*);
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// lua_detector_api.cc author Sourcefire Inc.
+
+#include "lua_detector_api.h"
+
+#include <cstring>
+
+#include <pcre.h>
+#include <lua.hpp>
+
+#include "hash/sfxhash.h"
+#include "log/messages.h"
+#include "main/snort_debug.h"
+#include "profiler/profiler.h"
+#include "sfip/sf_ip.h"
+#include "utils/util.h"
+
+#include "app_forecast.h"
+#include "app_info_table.h"
+#include "fw_appid.h"
+#include "host_port_app_cache.h"
+#include "http_common.h"
+#include "lua_detector_flow_api.h"
+#include "lua_detector_module.h"
+#include "lua_detector_util.h"
+#include "service_plugins/service_base.h"
+#include "service_plugins/service_ssl.h"
+#include "client_plugins/client_app_base.h"
+#include "detector_plugins/detector_dns.h"
+#include "detector_plugins/detector_pattern.h"
+
+#define DETECTOR "Detector"
+#define OVECCOUNT 30 /* should be a multiple of 3 */
+#define URL_LIST_STEP_SIZE 5000
+
+#define CHECK_INPUTS() \
+ if ( !checkServiceElement(ud) || !ud->validateParams.pkt ) \
+ { \
+ lua_pushnumber(L, SERVICE_ENULL); \
+ return 1; \
+ }
+
+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,
+};
+
+/*static const char * LuaLogLabel = "luaDetectorApi"; */
+
+ProfileStats luaDetectorsPerfStats;
+ProfileStats luaCiscoPerfStats;
+ProfileStats luaCustomPerfStats;
+
+static void FreeDetectorAppUrlPattern(DetectorAppUrlPattern* pattern);
+
+// FIXIT-H J lifetime of detector is easy to misuse with this idiom
+// Leaves 1 value (the Detector userdata) at the top of the stack
+Detector* createDetector(lua_State* L, const char* detectorName)
+{
+ auto detector = new Detector();
+ detector->myLuaState = L;
+ detector->name = detectorName;
+
+ UserData<Detector>::push(L, DETECTOR, detector);
+
+ // add a lua reference so the detector doesn't get garbage-collected
+ // FIXIT-M J should go in a different table maybe?
+ lua_pushvalue(L, -1);
+ detector->detectorUserDataRef = luaL_ref(L, LUA_REGISTRYINDEX);
+
+ return detector;
+}
+
+// must be called only when RNA is exitting.
+static void freeDetector(Detector* detector)
+{ delete detector; }
+
+// check service element, Allocate if necessary
+int checkServiceElement(Detector* detector)
+{
+ if ( !detector->server.pServiceElement )
+ {
+ detector->server.pServiceElement = new RNAServiceElement;
+ assert(detector->server.pServiceElement);
+ 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.
+//
+// lua params:
+// #1 - serviceName/stack - name of service
+// #2 - pValidator/stack - service validator function name
+// #3 - pFini/stack - service clean exit function name
+// return - a detector instance or none
+static int service_init(lua_State* L)
+{
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ // auto pServiceName = luaL_checkstring(L, 2);
+ auto pValidator = luaL_checkstring(L, 3);
+ auto pFini = luaL_checkstring(L, 4);
+
+ lua_getglobal(L, pValidator);
+ lua_getglobal(L, pFini);
+
+ if ( lua_isfunction(L, -1) && lua_isfunction(L, -2) )
+ {
+ if ( checkServiceElement(ud) )
+ {
+ ud->server.pServiceElement->validate = validateAnyService;
+ ud->server.pServiceElement->userdata = ud.ptr;
+ ud->server.pServiceElement->detectorType = DETECTOR_TYPE_DECODER;
+ }
+
+ lua_pop(L, 2);
+ return 1;
+ }
+ else
+ {
+ ErrorMessage("%s: attempted setting validator/fini to non-function\n",
+ ud->server.serviceModule.name);
+
+ lua_pop(L, 2);
+ return 0;
+ }
+}
+
+// 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.
+//
+// lua params:
+// #1 - detector/stack - detector object
+// #2 - protocol/stack - protocol type. Values can be {tcp=6, udp=17 }
+// #3 - pattern/stack - pattern string.
+// #4 - size/stack - number of bytes in pattern
+// #5 - position/stack - position offset where to start matching pattern.
+// return - status/stack - 0 if successful, -1 otherwise.
+static int service_registerPattern(lua_State* L)
+{
+ int index = 1;
+
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, index++);
+
+ // FIXIT-H J none of these params check for signedness casting issues
+ // FIXIT-M: May want to create a lua_toipprotocol() so we can handle
+ // error checking in that function.
+ int protocol = lua_tonumber(L, index++);
+ if (protocol > UINT8_MAX)
+ {
+ ErrorMessage("Invalid protocol value %d\n", protocol);
+ return -1;
+ }
+
+ const char* pattern = lua_tostring(L, index++);
+ size_t size = lua_tonumber(L, index++);
+ unsigned int position = lua_tonumber(L, index++);
+
+ /*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, (IpProtocol)protocol, (uint8_t*)pattern,
+ size, position, ud, ud->server.serviceModule.name);
+
+ lua_pushnumber(L, 0);
+ return 1;
+}
+
+static int common_registerAppId(lua_State* L)
+{
+ unsigned int appId;
+ int index = 1;
+
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, index++);
+ appId = lua_tonumber(L, index++);
+
+ if ( !ud->packageInfo.server.initFunctionName.empty() )
+ appSetLuaServiceValidator(
+ validateAnyService, appId, APPINFO_FLAG_SERVICE_ADDITIONAL, ud.ptr);
+
+ if ( !ud->packageInfo.client.initFunctionName.empty() )
+ appSetLuaClientValidator(
+ validateAnyClientApp, appId, APPINFO_FLAG_CLIENT_ADDITIONAL, ud.ptr);
+
+ appInfoSetActive(appId, true);
+
+ lua_pushnumber(L, 0);
+ return 1;
+}
+
+static int Detector_htons(lua_State* L)
+{
+ // FIXIT-L J ignoring arg #1, as it is unused
+ // auto* ud = UserData<Detector>::check(L, DETECTOR, 1);
+
+ unsigned short aShort = lua_tonumber(L, 2);
+
+ lua_pushnumber(L, htons(aShort));
+ return 1;
+}
+
+static int Detector_htonl(lua_State* L)
+{
+ // FIXIT-L J ignoring arg #1, as it is unused
+ // auto* ud = UserData<Detector>::check(L, DETECTOR, 1);
+
+ unsigned int anInt = lua_tonumber(L, 2);
+
+ 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')
+// lua params:
+// #1 - level - level of message. See DetectorCommon for enumeration.
+// #2 - message - message to be logged.
+static int Detector_logMessage(lua_State* L)
+{
+ const auto& name = (*UserData<Detector>::check(L, DETECTOR, 1))->server.serviceModule.name;
+
+ unsigned int level = lua_tonumber(L, 2);
+ const char* message = lua_tostring(L, 3);
+
+ switch ( level )
+ {
+ case LUA_LOG_CRITICAL:
+ FatalError("%s:%s\n", name, message);
+ break;
+
+ case LUA_LOG_ERR:
+ case LUA_LOG_WARN:
+ // FIXIT-L J should WARN do a WarningMessage instead?
+ ErrorMessage("%s:%s\n", name, message);
+ break;
+
+ case LUA_LOG_NOTICE:
+ case LUA_LOG_INFO:
+ LogMessage("%s:%s\n", name, message);
+ break;
+
+ case LUA_LOG_DEBUG:
+ DebugFormat(DEBUG_APPID, "%s:%s\n", name, message);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+// Analyze application payload
+// lua params:
+// 1 - detector/stack - detector object
+// 2 - major/stack - major number of application
+// 3 - minor/stack - minor number of application
+// 4 - flags/stack - any flags
+static int service_analyzePayload(lua_State* L)
+{
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+ unsigned int payloadId = lua_tonumber(L, 2);
+
+ assert(ud->validateParams.pkt);
+
+ ud->validateParams.flowp->payloadAppId = payloadId;
+
+ return 0;
+}
+
+// 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.
+int validateAnyService(ServiceValidationArgs* args)
+{
+ Profile lua_detector_context(luaCustomPerfStats);
+
+ int retValue;
+ auto detector = args->userdata;
+
+ if ( !detector )
+ {
+ // FIXIT-M J unhelpful error message
+ ErrorMessage("invalid LUA parameters");
+ return SERVICE_ENULL;
+ }
+
+ auto L = 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;
+
+ const auto& serverName = detector->name;
+
+ /*Note: Some frequently used header fields may be extracted and stored in detector for
+ better performance. */
+
+ if ( detector->packageInfo.server.validateFunctionName.empty() || !lua_checkstack(L, 1) )
+ {
+ ErrorMessage("server %s: invalid LUA %s\n", serverName.c_str(), lua_tostring(L, -1));
+ detector->validateParams.pkt = nullptr;
+ return SERVICE_ENULL;
+ }
+
+ lua_getglobal(L, detector->packageInfo.server.validateFunctionName.c_str());
+
+ DebugFormat(DEBUG_APPID, "server %s: Lua Memory usage %d\n",serverName.c_str(), lua_gc(L,
+ LUA_GCCOUNT, 0));
+ DebugFormat(DEBUG_APPID,"server %s: validating\n", serverName.c_str());
+
+ if ( lua_pcall(L, 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. */
+ ErrorMessage("server %s: error validating %s\n", serverName.c_str(), lua_tostring(L, -1));
+ detector->validateParams.pkt = nullptr;
+ return SERVICE_ENULL;
+ }
+
+ /**detectorFlows must be destroyed after each packet is processed.*/
+ sflist_static_free_all(&allocatedFlowList, freeDetectorFlow);
+
+ /* retrieve result */
+ if ( !lua_isnumber(L, -1) )
+ {
+ ErrorMessage("server %s: validator returned non-numeric value\n", serverName.c_str());
+ detector->validateParams.pkt = nullptr;
+ return SERVICE_ENULL;
+ }
+
+ retValue = lua_tonumber(L, -1);
+ lua_pop(L, 1); /* pop returned value */
+
+ DebugFormat(DEBUG_APPID, "server %s: Validator returned %d\n", serverName.c_str(), retValue);
+
+ detector->validateParams.pkt = nullptr;
+
+ 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 serviceName/stack - Name of service
+// @return serviceId/stack - serviceId if successful, -1 otherwise.
+static int service_getServiceId(lua_State* L)
+{
+ auto ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ lua_pushnumber(L, ud->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 protocol/stack - protocol type. Values can be {tcp=6, udp=17 }
+// @param port/stack - port number to register.
+// @return status/stack - 0 if successful, -1 otherwise.
+static int service_addPorts(lua_State* L)
+{
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ RNAServiceValidationPort pp;
+ pp.proto = (IpProtocol)lua_tonumber(L, 2);
+ pp.port = lua_tonumber(L, 3);
+ pp.reversed_validation = lua_tonumber(L, 5);
+ pp.validate = &validateAnyService;
+
+ if ( ((pp.proto != IpProtocol::UDP) && (pp.proto != IpProtocol::TCP)) || !pp.port )
+ {
+ lua_pushnumber(L, -1);
+ return 1;
+ }
+
+ if ( ServiceAddPort(&pp, &ud->server.serviceModule, ud, ud->pAppidNewConfig) )
+ {
+ lua_pushnumber(L, -1);
+ return 1;
+ }
+
+ ++ud->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.
+// @return status/stack - 0 if successful, -1 otherwise.
+static int service_removePorts(lua_State* L)
+{
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ detectorRemoveAllPorts(ud, ud->pAppidOldConfig);
+
+ lua_pushnumber(L, 0);
+ return 1;
+}
+
+// Shared function between Lua API and RNA core.
+void detectorRemoveAllPorts(Detector* detector, AppIdConfig* pConfig)
+{ ServiceRemovePorts(&validateAnyService, 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 serviceName/stack - Name of service
+// @return status/stack - 0 if successful, -1 otherwise.
+static int service_setServiceName(lua_State* L)
+{
+ lua_pushnumber(L, 0);
+ return 1;
+}
+
+/**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
+ )
+{
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ lua_pushstring(L, ud->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)
+{
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+ lua_pushnumber(L, ud->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)
+{
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ const char* pValidator = lua_tostring(L, 2);
+ lua_getglobal(L, pValidator);
+
+ if (!lua_isfunction(L, -1))
+ {
+ ErrorMessage("%s: attempted setting validator to non-function\n",
+ ud->server.serviceModule.name);
+
+ lua_pop(L, 1);
+ lua_pushnumber(L, -1);
+ return 1;
+ }
+
+ lua_pop(L, 1);
+
+ ud->packageInfo.server.validateFunctionName = pValidator;
+
+ 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)
+{
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+ uint16_t sport = lua_tonumber(L, 2);
+
+ /*check inputs and whether this function is called in context of a
+ packet */
+ if ( !checkServiceElement(ud) || !ud->validateParams.pkt )
+ {
+ lua_pushnumber(L, -1);
+ return 1;
+ }
+
+ AppIdFlowdataAddId(ud->validateParams.flowp, sport, ud->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;
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ serviceId = lua_tonumber(L, 2);
+ vendor = (char*)luaL_optstring(L, 3, nullptr);
+ version = (char*)luaL_optstring(L, 4, nullptr);
+
+ /*check inputs (vendor and version may be null) and whether this function is
+ called in context of a packet */
+ if ( !checkServiceElement(ud) || !ud->validateParams.pkt )
+ {
+ lua_pushnumber(L, SERVICE_ENULL);
+ return 1;
+ }
+
+ /*Phase2 - discuss RNAServiceSubtype will be maintained on lua side therefore the last
+ parameter on the following call is nullptr.
+ Subtype is not displayed on DC at present. */
+ retValue = AppIdServiceAddService(ud->validateParams.flowp, ud->validateParams.pkt,
+ ud->validateParams.dir, ud->server.pServiceElement,
+ appGetAppFromServiceId(serviceId, ud->pAppidActiveConfig), vendor, version, nullptr);
+
+ 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)
+{
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ CHECK_INPUTS();
+
+ unsigned int retValue = AppIdServiceFailService(ud->validateParams.flowp,
+ ud->validateParams.pkt,
+ ud->validateParams.dir, ud->server.pServiceElement, APPID_SESSION_DATA_NONE,
+ ud->pAppidActiveConfig);
+
+ 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)
+{
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ CHECK_INPUTS();
+
+ unsigned int retValue = AppIdServiceInProcess(ud->validateParams.flowp, ud->validateParams.pkt,
+ ud->validateParams.dir, ud->server.pServiceElement);
+
+ 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;
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ CHECK_INPUTS();
+
+ retValue = AppIdServiceIncompatibleData(ud->validateParams.flowp,
+ ud->validateParams.pkt,
+ ud->validateParams.dir, ud->server.pServiceElement,
+ APPID_SESSION_DATA_NONE, ud->pAppidActiveConfig);
+
+ 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
+ )
+{
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ lua_pushnumber(L, ud->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
+ )
+{
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ lua_pushnumber(L, ud->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
+ )
+{
+ char* pattern;
+ unsigned int offset;
+ pcre* re;
+ int ovector[OVECCOUNT];
+ const char* error;
+ int erroffset;
+ int rc, i;
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ pattern = (char*)lua_tostring(L, 2);
+ offset = lua_tonumber(L, 3); /*offset can be zero, no check necessary. */
+
+ {
+ /*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 */
+ nullptr); /*use default character tables */
+
+ if (re == nullptr)
+ {
+ ErrorMessage("PCRE compilation failed at offset %d: %s\n", erroffset, error);
+ return 0;
+ }
+
+ /*pattern match against the subject string. */
+ rc = pcre_exec(
+ re, /*compiled pattern */
+ nullptr, /*no extra data */
+ (char*)ud->validateParams.data, /*subject string */
+ ud->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); */
+ ErrorMessage("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*)ud->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
+ )
+{
+ char* pattern;
+ unsigned int patternLen;
+ unsigned int offset;
+ int rc;
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ pattern = (char*)lua_tostring(L, 2);
+ patternLen = lua_tonumber(L, 3);
+ offset = lua_tonumber(L, 4); /*offset can be zero, no check necessary. */
+
+ rc = memcmp((char*)ud->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)
+{
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ if ( !ud->validateParams.pkt || !ud->validateParams.pkt->has_ip() )
+ {
+ // FIXIT-H J why the inconsistent use of checkstack?
+ lua_checkstack (L, 1);
+ lua_pushnumber(L, 0);
+ return 1;
+ }
+
+ lua_checkstack (L, 1);
+ // FIXIT-H: is this conversion to double valid?
+ lua_pushnumber(L, (double)ud->validateParams.pkt->get_ip_proto_next() );
+ 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)
+{
+ const sfip_t* ipAddr;
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ ipAddr = ud->validateParams.pkt->ptrs.ip_api.get_src();
+
+ lua_checkstack (L, 1);
+ lua_pushnumber(L, ipAddr->ip32[0]);
+ 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)
+{
+ unsigned int port;
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ port = ud->validateParams.pkt->ptrs.sp;
+
+ 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)
+{
+ unsigned int port;
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ port = ud->validateParams.pkt->ptrs.dp;
+
+ 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)
+{
+ const sfip_t* ipAddr;
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ ipAddr = ud->validateParams.pkt->ptrs.ip_api.get_dst();
+
+ lua_checkstack (L, 1);
+ lua_pushnumber(L, ipAddr->ip32[0]);
+ 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)
+{
+ lua_checkstack (L, 1);
+ lua_pushnumber(L, app_id_processed_packet_count);
+ return 1;
+}
+
+CLIENT_APP_RETCODE validateAnyClientApp(
+ const uint8_t* data,
+ uint16_t size,
+ const int dir,
+ AppIdData* flowp,
+ Packet* pkt,
+ Detector* detector,
+ const AppIdConfig*
+ )
+{
+ Profile lua_profile_context(luaCustomPerfStats);
+
+ int retValue;
+ lua_State* myLuaState;
+ const char* validateFn;
+ const char* clientName;
+
+ if (!data || !flowp || !pkt || !detector)
+ {
+ return CLIENT_APP_ENULL;
+ }
+
+ myLuaState = detector->myLuaState;
+ detector->validateParams.data = data;
+ detector->validateParams.size = size;
+ detector->validateParams.dir = dir;
+ detector->validateParams.flowp = flowp;
+ detector->validateParams.pkt = (Packet*)pkt;
+ validateFn = detector->packageInfo.client.validateFunctionName.c_str();
+ clientName = detector->name.c_str();
+
+ if ((!validateFn) || !(lua_checkstack(myLuaState, 1)))
+ {
+ ErrorMessage("client %s: invalid LUA %s\n",clientName, lua_tostring(myLuaState, -1));
+ detector->validateParams.pkt = nullptr;
+ return CLIENT_APP_ENULL;
+ }
+
+ lua_getglobal(myLuaState, validateFn);
+
+ DebugFormat(DEBUG_APPID,"client %s: Lua Memory usage %d\n",clientName, lua_gc(myLuaState,
+ LUA_GCCOUNT,0));
+ DebugFormat(DEBUG_APPID,"client %s: validating\n",clientName);
+
+ if (lua_pcall(myLuaState, 0, 1, 0))
+ {
+ ErrorMessage("client %s: error validating %s\n",clientName, lua_tostring(myLuaState, -1));
+ detector->validateParams.pkt = nullptr;
+ return (CLIENT_APP_RETCODE)SERVICE_ENULL;
+ }
+
+ /**detectorFlows must be destroyed after each packet is processed.*/
+ sflist_static_free_all(&allocatedFlowList, freeDetectorFlow);
+
+ /* retrieve result */
+ if (!lua_isnumber(myLuaState, -1))
+ {
+ ErrorMessage("client %s: validator returned non-numeric value\n",clientName);
+ detector->validateParams.pkt = nullptr;
+ retValue = SERVICE_ENULL;
+ }
+
+ retValue = lua_tonumber(myLuaState, -1);
+ lua_pop(myLuaState, 1); /* pop returned value */
+ /*lua_settop(myLuaState, 0); */
+
+ DebugFormat(DEBUG_APPID,"client %s: Validator returned %d\n",clientName, retValue);
+
+ detector->validateParams.pkt = nullptr;
+
+ return (CLIENT_APP_RETCODE)retValue;
+}
+
+static int client_registerPattern(lua_State* L)
+{
+ IpProtocol protocol;
+ size_t size;
+ const char* pattern;
+ unsigned int position;
+ int index = 1;
+
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, index++);
+ protocol = (IpProtocol)lua_tonumber(L, index++);
+ pattern = lua_tostring(L, index++);
+ size = lua_tonumber(L, index++);
+ position = lua_tonumber(L, index++);
+
+ /*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. */
+
+ ud->client.appModule.userData = ud.ptr;
+ ClientAppLoadForConfigCallback((void*)&(ud->client.appModule),
+ &ud->pAppidNewConfig->clientAppConfig);
+ ClientAppRegisterPattern(
+ validateAnyClientApp, protocol, (const uint8_t*)pattern, size,
+ position, 0, ud, &ud->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*)
+{
+ /*nothing to do */
+ return 0;
+}
+
+static int service_addClient(lua_State* L)
+{
+ AppId clienAppId, serviceId;
+ const char* version;
+
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ clienAppId = lua_tonumber(L, 2);
+ serviceId = lua_tonumber(L, 3);
+ version = lua_tostring(L, 4);
+
+ if ( !ud->validateParams.pkt || !version )
+ {
+ lua_pushnumber(L, -1);
+ return 1;
+ }
+
+ AppIdAddClientApp(ud->validateParams.flowp, serviceId, clienAppId, version);
+
+ lua_pushnumber(L, 0);
+ return 1;
+}
+
+static int client_addApp(lua_State* L)
+{
+ unsigned int serviceId, productId;
+ const char* version;
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ serviceId = lua_tonumber(L, 2);
+ productId = lua_tonumber(L, 4);
+ version = lua_tostring(L, 5);
+
+ CHECK_INPUTS();
+
+ if ( !ud->client.appModule.api )
+ {
+ lua_pushnumber(L, -1);
+ return 1;
+ }
+
+ ud->client.appModule.api->add_app(ud->validateParams.flowp,
+ appGetAppFromServiceId(serviceId, ud->pAppidActiveConfig), appGetAppFromClientId(
+ productId, ud->pAppidActiveConfig), version);
+
+ lua_pushnumber(L, 0);
+ return 1;
+}
+
+static int client_addInfo(lua_State* L)
+{
+ const char* info;
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ info = lua_tostring(L, 2);
+
+ CHECK_INPUTS();
+
+ if (!ud->client.appModule.api)
+ {
+ lua_pushnumber(L, -1);
+ return 1;
+ }
+
+ ud->client.appModule.api->add_info(ud->validateParams.flowp, info);
+
+ lua_pushnumber(L, 0);
+ return 1;
+}
+
+static int client_addUser(lua_State* L)
+{
+ unsigned int serviceId;
+ const char* userName;
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ userName = lua_tostring(L, 2);
+ serviceId = lua_tonumber(L, 3);
+
+ CHECK_INPUTS();
+
+ if (!ud->client.appModule.api)
+ {
+ lua_pushnumber(L, -1);
+ return 1;
+ }
+
+ ud->client.appModule.api->add_user(ud->validateParams.flowp, userName,
+ appGetAppFromServiceId(serviceId, ud->pAppidActiveConfig), 1);
+
+ lua_pushnumber(L, 0);
+ return 1;
+}
+
+static int client_addPayload(lua_State* L)
+{
+ unsigned int payloadId;
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ payloadId = lua_tonumber(L, 2);
+
+ CHECK_INPUTS();
+
+ if (!ud->client.appModule.api)
+ {
+ lua_pushnumber(L, -1);
+ return 1;
+ }
+
+ ud->client.appModule.api->add_payload(ud->validateParams.flowp,
+ appGetAppFromPayloadId(payloadId, ud->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)
+{
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ CHECK_INPUTS();
+
+ auto df = new DetectorFlow();
+ df->pFlow = ud->validateParams.flowp;
+
+ UserData<DetectorFlow>::push(L, "DetectorFlow", df);
+
+ return 1;
+}
+
+int Detector_addHttpPattern(lua_State* L)
+{
+ int index = 1;
+
+ /* Verify detector user data and that we are not in packet context */
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, index++);
+
+ /* Verify valid pattern type */
+ enum httpPatternType pType = (enum httpPatternType)lua_tointeger(L, index++);
+ if (pType < HTTP_PAYLOAD || pType > HTTP_URL)
+ {
+ ErrorMessage("Invalid HTTP pattern type.");
+ return 0;
+ }
+
+ /* Verify valid DHSequence */
+ DHPSequence seq = (DHPSequence)lua_tointeger(L, index++);
+ if (seq < SINGLE || seq > USER_AGENT_HEADER)
+ {
+ ErrorMessage("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++);
+
+ // FIXIT-H J should this be inverted?
+ if (ud->validateParams.pkt)
+ {
+ ErrorMessage(
+ "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;
+ const char* tmpString = lua_tolstring(L, index++, &pattern_size);
+ if ( tmpString == nullptr || pattern_size == 0)
+ {
+ ErrorMessage("Invalid HTTP pattern string.");
+ return 0;
+ }
+
+ uint8_t* pattern_str = (uint8_t*)snort_strdup(tmpString);
+ uint32_t appId = lua_tointeger(L, index++);
+
+ HTTPListElement* element = (HTTPListElement*)snort_calloc(sizeof(HTTPListElement));
+ DetectorHTTPPattern* pattern = &element->detectorHTTPPattern;
+ AppIdConfig* pConfig = ud->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(<appId>, '<pattern string>' )
+*/
+int Detector_addSSLCertPattern(lua_State* L)
+{
+ uint8_t* pattern_str;
+ size_t pattern_size;
+ int index = 1;
+ uint8_t type;
+ AppId app_id;
+
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, index++);
+ if ( ud->validateParams.pkt )
+ {
+ ErrorMessage("Invalid SSL detector user data or context.");
+ return 0;
+ }
+
+ type = lua_tointeger(L, index++);
+ app_id = (AppId)lua_tointeger(L, index++);
+
+ pattern_size = 0;
+ const char* tmpString = lua_tolstring(L, index++, &pattern_size);
+ if (!tmpString || !pattern_size)
+ {
+ ErrorMessage("Invalid SSL Host pattern string");
+ return 0;
+ }
+ pattern_str = (uint8_t*)snort_strdup(tmpString);
+
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ if (!ssl_add_cert_pattern(pattern_str, pattern_size, type, app_id,
+ &ud->pAppidNewConfig->serviceSslConfig))
+ {
+ snort_free(pattern_str);
+ ErrorMessage("Failed to add an SSL pattern list member");
+ return 0;
+ }
+#else
+ UNUSED(pattern_str);
+ UNUSED(type);
+#endif
+
+ appInfoSetActive(app_id, true);
+ return 0;
+}
+
+/* On the lua side, this should look something like:
+ addDNSHostPattern(<appId>, '<pattern string>' )
+*/
+int Detector_addDNSHostPattern(lua_State* L)
+{
+ uint8_t* pattern_str;
+ size_t pattern_size;
+ int index = 1;
+ uint8_t type;
+ AppId app_id;
+
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, index++);
+ if ( ud->validateParams.pkt )
+ {
+ ErrorMessage("LuaDetectorApi:Invalid DNS detector user data or context.");
+ return 0;
+ }
+
+ type = lua_tointeger(L, index++);
+ app_id = (AppId)lua_tointeger(L, index++);
+
+ pattern_size = 0;
+ const char* tmpString = lua_tolstring(L, index++, &pattern_size);
+ if (!tmpString || !pattern_size)
+ {
+ ErrorMessage("LuaDetectorApi:Invalid DNS Host pattern string");
+ return 0;
+ }
+ pattern_str = (uint8_t*)snort_strdup(tmpString);
+ if (!dns_add_host_pattern(pattern_str, pattern_size, type, app_id,
+ &ud->pAppidNewConfig->serviceDnsConfig))
+ {
+ snort_free(pattern_str);
+ ErrorMessage("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;
+ AppId app_id;
+
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, index++);
+ if ( ud->validateParams.pkt )
+ {
+ ErrorMessage("Invalid SSL detector user data or context.");
+ return 0;
+ }
+
+ type = lua_tointeger(L, index++);
+ app_id = (AppId)lua_tointeger(L, index++);
+
+ pattern_size = 0;
+ const char* tmpString = lua_tolstring(L, index++, &pattern_size);
+ if (!tmpString || !pattern_size)
+ {
+ ErrorMessage("Invalid SSL Host pattern string");
+ return 0;
+ }
+ pattern_str = (uint8_t*)snort_strdup(tmpString);
+
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ if (!ssl_add_cname_pattern(pattern_str, pattern_size, type, app_id,
+ &ud->pAppidNewConfig->serviceSslConfig))
+ {
+ snort_free(pattern_str);
+ ErrorMessage("Failed to add an SSL pattern list member");
+ return 0;
+ }
+#else
+ UNUSED(pattern_str);
+ UNUSED(type);
+#endif
+
+ 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;
+ AppId app_id;
+ in6_addr ip6Addr;
+
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, index++);
+ if ( ud->validateParams.pkt )
+ {
+ ErrorMessage("%s: Invalid detector user data or context.\n",__func__);
+ return 0;
+ }
+
+ type = lua_tointeger(L, index++);
+ app_id = (AppId)lua_tointeger(L, index++);
+
+ ipaddr_size = 0;
+ const char* tmpString = lua_tolstring(L, index++, &ipaddr_size);
+ if (!tmpString || !ipaddr_size)
+ {
+ ErrorMessage("%s:Invalid ipaddr string\n",__func__);
+ return 0;
+ }
+ if (!strchr(tmpString, ':'))
+ {
+ if (inet_pton(AF_INET, tmpString, &ip6Addr) <= 0)
+ {
+ ErrorMessage("%s: Invalid IP address: %s\n",__func__, tmpString);
+ return 0;
+ }
+
+ // FIXIT-H J ip6Addr type is struct in6_addr, so...
+ // ip6Addr.u6_addr32[0] = ip6Addr.u6_addr32[1] = 0;
+ // ip6Addr.u6_addr32[2] = ntohl(0x0000ffff);
+ }
+ else
+ {
+ if (inet_pton(AF_INET6, tmpString, &ip6Addr) <= 0)
+ {
+ ErrorMessage("%s: Invalid IP address: %s\n",__func__, tmpString);
+ return 0;
+ }
+ }
+ unsigned port = lua_tointeger(L, index++);
+ unsigned proto = lua_tointeger(L, index++);
+
+ if (proto > UINT8_MAX)
+ {
+ ErrorMessage("%s:Invalid protocol value %d\n",__func__, proto);
+ return 0;
+ }
+
+ if (!hostPortAppCacheAdd(&ip6Addr, (uint16_t)port, (IpProtocol)proto, type, app_id,
+ ud->pAppidNewConfig))
+ {
+ ErrorMessage("%s:Failed to backend call\n",__func__);
+ }
+
+ return 0;
+}
+
+static int Detector_addContentTypePattern(lua_State* L)
+{
+ uint8_t* pattern;
+ AppId appId;
+ int index = 1;
+
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, index++);
+
+ size_t stringSize = 0;
+ const char* tmpString = lua_tolstring(L, index++, &stringSize);
+ if (!tmpString || !stringSize)
+ {
+ ErrorMessage("Invalid HTTP Header string");
+ return 0;
+ }
+ pattern = (uint8_t*)snort_strdup(tmpString);
+ appId = lua_tointeger(L, index++);
+
+ if (ud->validateParams.pkt)
+ {
+ ErrorMessage("Invalid detector context addSipUserAgent: appId %d\n",appId);
+ snort_free(pattern);
+ return 0;
+ }
+
+ HTTPListElement* element = (HTTPListElement*)snort_calloc(sizeof(HTTPListElement));
+ DetectorHTTPPattern* detector = &element->detectorHTTPPattern;
+ AppIdConfig* pConfig = ud->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,
+ UserData<Detector>** detector_user_data, const char* errorString)
+{
+ // Verify detector user data and that we are not in packet context
+ *detector_user_data = UserData<Detector>::check(L, DETECTOR, index);
+ if (!*detector_user_data || (**detector_user_data)->validateParams.pkt)
+ {
+ ErrorMessage("%s", errorString);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int detector_create_chp_app(UserData<Detector>* ud, AppId appIdInstance,
+ unsigned app_type_flags, int num_matches)
+{
+ CHPApp* new_app = (CHPApp*)snort_calloc(sizeof(CHPApp));
+ new_app->appIdInstance = appIdInstance;
+ new_app->app_type_flags = app_type_flags;
+ new_app->num_matches = num_matches;
+
+ if (sfxhash_add((*ud)->pAppidNewConfig->CHP_glossary,
+ &(new_app->appIdInstance), new_app))
+ {
+ ErrorMessage("LuaDetectorApi:Failed to add CHP for appId %d, instance %d",
+ CHP_APPIDINSTANCE_TO_ID(appIdInstance), CHP_APPIDINSTANCE_TO_INSTANCE(appIdInstance));
+ snort_free(new_app);
+ return -1;
+ }
+ return 0;
+}
+
+static int Detector_CHPCreateApp(lua_State* L)
+{
+ UserData<Detector>* ud;
+ AppId appId;
+ unsigned app_type_flags;
+ int num_matches;
+
+ AppId appIdInstance;
+
+ int index = 1;
+
+ if (GetDetectorUserData(L, index++, &ud,
+ "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((*ud)->pAppidNewConfig->CHP_glossary, &appIdInstance))
+ {
+ ErrorMessage(
+ "LuaDetectorApi:Attempt to add more than one CHP for appId %d - use CHPMultiCreateApp",
+ appId);
+ return 0;
+ }
+
+ detector_create_chp_app(ud, 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)
+ {
+ ErrorMessage("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 = nullptr; // Lua owns this pointer
+ *pattern_size = 0;
+ *pattern_data = nullptr;
+ tmpString = lua_tolstring(L, index, &*pattern_size);
+ // FIXIT-M: recode all this to something elegant since snort_strdup can't fail (just like Rudy)
+ // non-empty pattern required
+ if (!tmpString || !*pattern_size || !(*pattern_data = snort_strdup(tmpString)))
+ {
+ if (*pattern_size) // implies snort_strdup() failed
+ ErrorMessage("LuaDetectorApi:CHP Action PATTERN string mem alloc failed.");
+ else
+ ErrorMessage("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)
+ {
+ ErrorMessage("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 nullptr pointer because the action data is optional
+ const char* tmpString = nullptr; // Lua owns this pointer
+ size_t action_data_size = 0;
+ tmpString = lua_tolstring(L, index, &action_data_size);
+ if (action_data_size)
+ *action_data = snort_strdup(tmpString);
+ else
+ *action_data = nullptr;
+
+ return 0;
+}
+
+static int detector_add_chp_action(UserData<Detector>* ud,
+ AppId appIdInstance, int isKeyPattern, PatternType patternType,
+ size_t patternSize, char* patternData, ActionType actionType, char* optionalActionData)
+{
+ uint precedence;
+ CHPListElement* tmp_chpa, * chpa;
+ CHPApp* chpapp;
+
+ //find the CHP App for this
+ if (!(chpapp = (decltype(chpapp))sfxhash_find((*ud)->pAppidNewConfig->CHP_glossary,
+ &appIdInstance)))
+ {
+ ErrorMessage(
+ "LuaDetectorApi:Invalid attempt to add a CHP action for unknown appId %d, instance %d. - pattern:\"%s\" - action \"%s\"",
+ CHP_APPIDINSTANCE_TO_ID(appIdInstance), CHP_APPIDINSTANCE_TO_INSTANCE(appIdInstance),
+ patternData, optionalActionData ? optionalActionData : "");
+ snort_free(patternData);
+ if (optionalActionData)
+ snort_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]++;
+ // 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)
+ chpapp->ptype_rewrite_insert_used[patternType]=1; // true.
+ else if (actionType != ALTERNATE_APPID)
+ chpapp->ptype_req_counts[patternType]++;
+
+ chpa = (CHPListElement*)snort_calloc(sizeof(CHPListElement));
+ 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
+
+ AppIdConfig* pConfig = (*ud)->pAppidNewConfig;
+
+ 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);
+ }
+ return 0;
+}
+
+static int Detector_CHPAddAction(lua_State* L)
+{
+ UserData<Detector>* ud;
+ int key_pattern;
+ PatternType ptype;
+ size_t psize;
+ char* pattern;
+ ActionType action;
+ char* action_data;
+
+ AppId appIdInstance;
+ AppId appId;
+
+ int index = 1;
+
+ if (GetDetectorUserData(L, index++, &ud,
+ "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))
+ {
+ snort_free(pattern);
+ return 0;
+ }
+
+ // Parameter 6
+ if (CHPGetActionData(L, index++, &action_data))
+ {
+ snort_free(pattern);
+ return 0;
+ }
+
+ return detector_add_chp_action(ud, appIdInstance, key_pattern, ptype,
+ psize, pattern, action, action_data);
+}
+
+static int Detector_CHPMultiCreateApp(lua_State* L)
+{
+ UserData<Detector>* ud;
+ AppId appId;
+ unsigned app_type_flags;
+ int num_matches;
+
+ AppId appIdInstance;
+ int instance;
+
+ int index = 1;
+
+ if (GetDetectorUserData(L, index++, &ud,
+ "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((*ud)->pAppidNewConfig->CHP_glossary,
+ &appIdInstance))
+ continue;
+ break;
+ }
+
+ // We only want a maximum of these for each appId.
+ if (instance == CHP_APPID_INSTANCE_MAX)
+ {
+ ErrorMessage("LuaDetectorApi:Attempt to create more than %d CHP for appId %d",
+ CHP_APPID_INSTANCE_MAX, appId);
+ return 0;
+ }
+
+ if (detector_create_chp_app(ud, appIdInstance, app_type_flags, num_matches))
+ return 0;
+
+ lua_pushnumber(L, appIdInstance);
+ return 1;
+}
+
+static int Detector_CHPMultiAddAction(lua_State* L)
+{
+ UserData<Detector>* ud;
+ int key_pattern;
+ PatternType ptype;
+ size_t psize;
+ char* pattern;
+ ActionType action;
+ char* action_data;
+
+ AppId appIdInstance;
+
+ int index = 1;
+
+ if (GetDetectorUserData(L, index++, &ud,
+ "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))
+ {
+ snort_free(pattern);
+ return 0;
+ }
+
+ // Parameter 6
+ if (CHPGetActionData(L, index++, &action_data))
+ {
+ snort_free(pattern);
+ return 0;
+ }
+
+ return detector_add_chp_action(ud, 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
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, index++);
+ if ( ud->validateParams.pkt )
+ {
+ ErrorMessage("LuaDetectorApi:Invalid HTTP detector user data in addPortOnlyService.");
+ return 0;
+ }
+
+ AppId appId = lua_tointeger(L, index++);
+ u_int16_t port = lua_tointeger(L, index++);
+ u_int8_t protocol = lua_tointeger(L, index++);
+
+ if (port == 0)
+ ud->pAppidNewConfig->ip_protocol[protocol] = appId;
+ else if (protocol == 6)
+ ud->pAppidNewConfig->tcp_port_only[port] = appId;
+ else if (protocol == 17)
+ ud->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;
+ LengthKey length_sequence;
+ int index = 1;
+
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, index++);
+
+ AppId appId = lua_tonumber(L, index++);
+ IpProtocol proto = (IpProtocol)lua_tonumber(L, index++);
+ uint8_t sequence_cnt = lua_tonumber(L, index++);
+ const char* sequence_str = lua_tostring(L, index++);
+
+ if (((proto != IpProtocol::TCP) && (proto != IpProtocol::UDP))
+ || ((sequence_cnt == 0) || (sequence_cnt > LENGTH_SEQUENCE_CNT_MAX))
+ || ((sequence_str == nullptr) || (strlen(sequence_str) == 0)))
+ {
+ ErrorMessage("LuaDetectorApi:Invalid input (%d,%u,%u,\"%s\")!",
+ appId, (unsigned)proto, (unsigned)sequence_cnt, sequence_str ? 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:
+ ErrorMessage("LuaDetectorApi:Invalid sequence string (\"%s\")!",
+ sequence_str);
+ lua_pushnumber(L, -1);
+ return 1;
+ }
+ str_ptr++;
+
+ if (*str_ptr != '/')
+ {
+ ErrorMessage("LuaDetectorApi:Invalid sequence string (\"%s\")!",
+ sequence_str);
+ lua_pushnumber(L, -1);
+ return 1;
+ }
+ str_ptr++;
+
+ length = (uint16_t)atoi(str_ptr);
+ if (length == 0)
+ {
+ ErrorMessage("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)))
+ {
+ ErrorMessage("LuaDetectorApi:Invalid sequence string (\"%s\")!",
+ sequence_str);
+ lua_pushnumber(L, -1);
+ return 1;
+ }
+ str_ptr++;
+ }
+
+ if (!lengthAppCacheAdd(&length_sequence, appId, ud->pAppidNewConfig))
+ {
+ ErrorMessage("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;
+
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, index++);
+ if ( ud->validateParams.pkt )
+ {
+ ErrorMessage("LuaDetectorApi:Invalid HTTP detector user data in AFAddApp.");
+ return 0;
+ }
+
+ ApplicationId indicator = (ApplicationId)lua_tointeger(L, index++);
+ ApplicationId forecast = (ApplicationId)lua_tointeger(L, index++);
+ ApplicationId target = (ApplicationId)lua_tointeger(L, index++);
+
+ if (sfxhash_find(ud->pAppidNewConfig->AF_indicators, &indicator))
+ {
+ ErrorMessage("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(ud->pAppidNewConfig->AF_indicators, &indicator, &val))
+ {
+ ErrorMessage("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 */
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, index++);
+ if ( ud->validateParams.pkt )
+ {
+ ErrorMessage("Invalid HTTP detector user data in addAppUrl.");
+ return 0;
+ }
+
+ u_int32_t service_id = lua_tointeger(L, index++);
+ u_int32_t client_app = lua_tointeger(L, index++);
+ /*u_int32_t client_app_type =*/ lua_tointeger(L, index++);
+ u_int32_t payload = lua_tointeger(L, index++);
+ /*u_int32_t payload_type =*/ lua_tointeger(L, index++);
+
+ if (ud->validateParams.pkt)
+ {
+ ErrorMessage(
+ "Invalid HTTP detector context addAppUrl: service_id %u; client_app %u; payload %u\n",
+ service_id, client_app, payload);
+ return 0;
+ }
+
+ /* Verify that host pattern is a valid string */
+ size_t hostPatternSize = 0;
+ u_int8_t* hostPattern = nullptr;
+ tmpString = lua_tolstring(L, index++, &hostPatternSize);
+ if (!tmpString || !hostPatternSize)
+ {
+ ErrorMessage("Invalid host pattern string.");
+ return 0;
+ }
+ else
+ hostPattern = (u_int8_t*)snort_strdup(tmpString);
+
+ /* Verify that path pattern is a valid string */
+ size_t pathPatternSize = 0;
+ u_int8_t* pathPattern = nullptr;
+ tmpString = lua_tolstring(L, index++, &pathPatternSize);
+ if (!tmpString || !pathPatternSize )
+ {
+ ErrorMessage("Invalid path pattern string.");
+ snort_free(hostPattern);
+ return 0;
+ }
+ else
+ pathPattern = (u_int8_t*)snort_strdup(tmpString);
+
+ /* Verify that scheme pattern is a valid string */
+ size_t schemePatternSize;
+ u_int8_t* schemePattern = nullptr;
+ tmpString = lua_tolstring(L, index++, &schemePatternSize);
+ if (!tmpString || !schemePatternSize )
+ {
+ ErrorMessage("Invalid scheme pattern string.");
+ snort_free(pathPattern);
+ snort_free(hostPattern);
+ return 0;
+ }
+ else
+ schemePattern = (u_int8_t*)snort_strdup(tmpString);
+
+ /* Verify that query pattern is a valid string */
+ size_t queryPatternSize;
+ u_int8_t* queryPattern = nullptr;
+ tmpString = lua_tolstring(L, index++, &queryPatternSize);
+ if (tmpString && queryPatternSize)
+ queryPattern = (u_int8_t*)snort_strdup(tmpString);
+ else
+ {
+ ErrorMessage("Invalid query pattern string.");
+ snort_free(hostPattern);
+ snort_free(pathPattern);
+ snort_free(schemePattern);
+ return 0;
+ }
+
+ u_int32_t appId = lua_tointeger(L, index++);
+
+ /* Allocate memory for data structures */
+ DetectorAppUrlPattern* pattern = (DetectorAppUrlPattern*)snort_calloc(
+ sizeof(DetectorAppUrlPattern));
+ AppIdConfig* pConfig = ud->pAppidNewConfig;
+
+ pattern->userData.service_id = appGetAppFromServiceId(service_id, pConfig);
+ pattern->userData.client_app = appGetAppFromClientId(client_app, pConfig);
+ pattern->userData.payload = appGetAppFromPayloadId(payload, 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 nullptr.
+ * This case is same as malloc. In case of error, realloc will return nullptr, and
+ * original urlPattern buffer is left untouched.
+ */
+ if (urlList->usedCount == urlList->allocatedCount)
+ {
+ tmp = (decltype(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 */
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, index++);
+ if ( ud->validateParams.pkt )
+ {
+ ErrorMessage("Invalid HTTP detector user data in addRTMPUrl.");
+ return 0;
+ }
+
+ u_int32_t service_id = lua_tointeger(L, index++);
+ u_int32_t client_app = lua_tointeger(L, index++);
+ /*u_int32_t client_app_type =*/ lua_tointeger(L, index++);
+ u_int32_t payload = lua_tointeger(L, index++);
+ /*u_int32_t payload_type =*/ lua_tointeger(L, index++);
+
+ if (ud->validateParams.pkt)
+ {
+ ErrorMessage(
+ "Invalid HTTP detector context addRTMPUrl: service_id %u; client_app %u; payload %u\n",
+ service_id, client_app, payload);
+ return 0;
+ }
+
+ /* Verify that host pattern is a valid string */
+ size_t hostPatternSize = 0;
+ u_int8_t* hostPattern = nullptr;
+ tmpString = lua_tolstring(L, index++, &hostPatternSize);
+ // FIXIT - recode all this to something elegant since snort_strdup can't fail (just like Rudy)
+ if (!tmpString || !hostPatternSize || !(hostPattern = (u_int8_t*)snort_strdup(tmpString)))
+ {
+ ErrorMessage("Invalid host pattern string.");
+ return 0;
+ }
+
+ /* Verify that path pattern is a valid string */
+ size_t pathPatternSize = 0;
+ u_int8_t* pathPattern = nullptr;
+ tmpString = lua_tolstring(L, index++, &pathPatternSize);
+ // FIXIT - recode all this to something elegant since snort_strdup can't fail (just like Rudy)
+ if (!tmpString || !pathPatternSize || !(pathPattern = (u_int8_t*)snort_strdup(tmpString)))
+ {
+ ErrorMessage("Invalid path pattern string.");
+ snort_free(hostPattern);
+ return 0;
+ }
+
+ /* Verify that scheme pattern is a valid string */
+ size_t schemePatternSize;
+ u_int8_t* schemePattern = nullptr;
+ tmpString = lua_tolstring(L, index++, &schemePatternSize);
+ // FIXIT - recode all this to something elegant since snort_strdup can't fail (just like Rudy)
+ if (!tmpString || !schemePatternSize || !(schemePattern = (u_int8_t*)snort_strdup(tmpString)))
+ {
+ ErrorMessage("Invalid scheme pattern string.");
+ snort_free(pathPattern);
+ snort_free(hostPattern);
+ return 0;
+ }
+
+ /* Verify that query pattern is a valid string */
+ size_t queryPatternSize;
+ u_int8_t* queryPattern = nullptr;
+ tmpString = lua_tolstring(L, index++, &queryPatternSize);
+ if (tmpString && queryPatternSize)
+ queryPattern = (u_int8_t*)snort_strdup(tmpString);
+
+ u_int32_t appId = lua_tointeger(L, index++);
+
+ /* Allocate memory for data structures */
+ DetectorAppUrlPattern* pattern = (DetectorAppUrlPattern*)snort_calloc(
+ sizeof(DetectorAppUrlPattern));
+
+ /* 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_app;
+ pattern->userData.payload = payload;
+ 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;
+
+ AppIdConfig* pConfig = ud->pAppidNewConfig;
+ DetectorAppUrlList* urlList = &pConfig->httpPatternLists.RTMPUrlList;
+
+ /**first time usedCount and allocatedCount are both 0, urlPattern will be nullptr.
+ * This case is same as malloc. In case of error, realloc will return nullptr, and
+ * original urlPattern buffer is left untouched.
+ */
+ if (urlList->usedCount == urlList->allocatedCount)
+ {
+ tmp = (decltype(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 <clienAppId, clientVersion, multi-Pattern> format. */
+static int Detector_addSipUserAgent(lua_State* L)
+{
+ int index = 1;
+
+ /* Verify detector user data and that we are not in packet context */
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, index++);
+
+ u_int32_t client_app = lua_tointeger(L, index++);
+ const char* clientVersion = lua_tostring(L, index++);
+ if (!clientVersion )
+ {
+ ErrorMessage("Invalid sip client version string.");
+ return 0;
+ }
+
+ if (ud->validateParams.pkt)
+ {
+ ErrorMessage("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)
+ {
+ ErrorMessage("Invalid sip ua pattern string.");
+ return 0;
+ }
+
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ sipUaPatternAdd(client_app, clientVersion, uaPattern,
+ &ud->pAppidNewConfig->detectorSipConfig);
+#endif
+
+ 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 */
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, index++);
+ if ( ud->validateParams.pkt )
+ {
+ ErrorMessage("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)
+ {
+ ErrorMessage("Invalid appName string.");
+ lua_pushnumber(L, APP_ID_NONE);
+ return 1; /*number of results */
+ }
+
+ AppInfoTableEntry* entry = appInfoEntryCreate(tmpString,
+ ud->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, clienAppId;
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ serviceAppId = lua_tonumber(L, 2);
+ clienAppId = lua_tonumber(L, 3);
+
+ /*check inputs and whether this function is called in context of a
+ packet */
+ if ( !ud->validateParams.pkt )
+ {
+ lua_pushnumber(L, -1);
+ return 1;
+ }
+
+ if (!ud->client.appModule.api)
+ {
+ lua_pushnumber(L, -1);
+ return 1;
+ }
+
+ ud->client.appModule.api->add_app(ud->validateParams.flowp, serviceAppId,
+ clienAppId, "");
+
+ 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;
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ serviceId = lua_tonumber(L, 2);
+
+ CHECK_INPUTS();
+
+ /*Phase2 - discuss RNAServiceSubtype will be maintained on lua side therefore the last
+ parameter on the following call is nullptr.
+ Subtype is not displayed on DC at present. */
+ retValue = AppIdServiceAddService(ud->validateParams.flowp, ud->validateParams.pkt,
+ ud->validateParams.dir, ud->server.pServiceElement,
+ serviceId, nullptr, nullptr, nullptr);
+
+ lua_pushnumber(L, retValue);
+ return 1;
+}
+
+static int openAddPayloadApp(lua_State* L)
+{
+ unsigned int payloadAppId;
+
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ payloadAppId = lua_tonumber(L, 2);
+
+ /*check inputs and whether this function is called in context of a
+ packet */
+ if ( !ud->validateParams.pkt )
+ {
+ lua_pushnumber(L, -1);
+ return 1;
+ }
+
+ if (!ud->client.appModule.api)
+ {
+ lua_pushnumber(L, -1);
+ return 1;
+ }
+
+ ud->client.appModule.api->add_payload(ud->validateParams.flowp, payloadAppId);
+
+ lua_pushnumber(L, 0);
+ return 1;
+}
+
+int openAddHttpPattern(lua_State* L)
+{
+ int index = 1;
+ AppIdConfig* pConfig;
+
+ /* Verify detector user data and that we are not in packet context */
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, index++);
+
+ pConfig = ud->pAppidNewConfig;
+
+ /* Verify valid pattern type */
+ enum httpPatternType pType = (enum httpPatternType)lua_tointeger(L, index++);
+ if (pType < HTTP_PAYLOAD || pType > HTTP_URL)
+ {
+ ErrorMessage("Invalid HTTP pattern type.");
+ return 0;
+ }
+
+ /* Verify valid DHSequence */
+ DHPSequence seq = (DHPSequence)lua_tointeger(L, index++);
+ if (seq < SINGLE || seq > USER_AGENT_HEADER)
+ {
+ ErrorMessage("Invalid HTTP DHP Sequence.");
+ return 0;
+ }
+
+ uint32_t serviceAppId = lua_tointeger(L, index++);
+ uint32_t clienAppId = lua_tointeger(L, index++);
+ uint32_t payloadAppId = lua_tointeger(L, index++);
+
+ if (ud->validateParams.pkt)
+ {
+ ErrorMessage(
+ "Invalid detector context addHttpPattern: serviceAppId %u; clienAppId %u; payloadAppId %u\n",
+ serviceAppId, clienAppId, payloadAppId);
+ return 0;
+ }
+
+ /* Verify that pattern is a valid string */
+ size_t pattern_size = 0;
+ uint8_t* pattern_str = (uint8_t*)snort_strdup(lua_tolstring(L, index++, &pattern_size));
+ if (pattern_str == nullptr || pattern_size == 0)
+ {
+ ErrorMessage("Invalid HTTP pattern string.");
+ snort_free(pattern_str);
+ return 0;
+ }
+
+ HTTPListElement* element = (HTTPListElement*)snort_calloc(sizeof(HTTPListElement));
+ DetectorHTTPPattern* pattern = &element->detectorHTTPPattern;
+ pattern->seq = seq;
+ pattern->service_id = serviceAppId;
+ pattern->client_app = clienAppId;
+ 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(clienAppId, 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 */
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, index++);
+ if ( ud->validateParams.pkt )
+ {
+ ErrorMessage("Invalid HTTP detector user data in addAppUrl.");
+ return 0;
+ }
+
+ AppIdConfig* pConfig = ud->pAppidNewConfig;
+ u_int32_t serviceAppId = lua_tointeger(L, index++);
+ u_int32_t clienAppId = lua_tointeger(L, index++);
+ u_int32_t payloadAppId = lua_tointeger(L, index++);
+
+ if (ud->validateParams.pkt)
+ {
+ ErrorMessage(
+ "Invalid HTTP detector context addAppUrl: serviceAppId %u; clienAppId %u; payloadAppId %u\n",
+ serviceAppId, clienAppId, payloadAppId);
+ return 0;
+ }
+
+ /* Verify that host pattern is a valid string */
+ size_t hostPatternSize = 0;
+ u_int8_t* hostPattern = nullptr;
+ tmpString = lua_tolstring(L, index++, &hostPatternSize);
+ if (!tmpString || !hostPatternSize || !(hostPattern = (u_int8_t* )snort_strdup(tmpString)))
+ {
+ ErrorMessage("Invalid host pattern string.");
+ return 0;
+ }
+
+ /* Verify that path pattern is a valid string */
+ size_t pathPatternSize = 0;
+ u_int8_t* pathPattern = nullptr;
+ tmpString = lua_tolstring(L, index++, &pathPatternSize);
+ if (!tmpString || !pathPatternSize || !(pathPattern = (u_int8_t*)snort_strdup(tmpString)))
+ {
+ ErrorMessage("Invalid path pattern string.");
+ snort_free(hostPattern);
+ return 0;
+ }
+
+ /* Verify that scheme pattern is a valid string */
+ size_t schemePatternSize;
+ u_int8_t* schemePattern = nullptr;
+ tmpString = lua_tolstring(L, index++, &schemePatternSize);
+ if (!tmpString || !schemePatternSize || !(schemePattern = (u_int8_t*)snort_strdup(tmpString)))
+ {
+ ErrorMessage("Invalid scheme pattern string.");
+ snort_free(pathPattern);
+ snort_free(hostPattern);
+ return 0;
+ }
+
+ /* Allocate memory for data structures */
+ DetectorAppUrlPattern* pattern = (DetectorAppUrlPattern*)snort_calloc(
+ sizeof(DetectorAppUrlPattern));
+ pattern->userData.service_id = serviceAppId;
+ pattern->userData.client_app = clienAppId;
+ pattern->userData.payload = payloadAppId;
+ pattern->userData.appId = APP_ID_NONE;
+ pattern->userData.query.pattern = nullptr;
+ 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 nullptr.
+ * This case is same as malloc. In case of error, realloc will return nullptr, and
+ * original urlPattern buffer is left untouched.
+ */
+ if (urlList->usedCount == urlList->allocatedCount)
+ {
+ tmp = (decltype(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(serviceAppId, true);
+ appInfoSetActive(clienAppId, true);
+ appInfoSetActive(payloadAppId, true);
+
+ return 0;
+}
+
+void CleanClientPortPatternList(AppIdConfig* pConfig)
+{
+ PortPatternNode* tmp;
+
+ if ( pConfig->clientPortPattern)
+ {
+ while ((tmp = pConfig->clientPortPattern->luaInjectedPatterns))
+ {
+ pConfig->clientPortPattern->luaInjectedPatterns = tmp->next;
+ snort_free(tmp->pattern);
+ snort_free(tmp->detectorName);
+ snort_free(tmp);
+ }
+
+ snort_free(pConfig->clientPortPattern);
+ }
+}
+
+void CleanServicePortPatternList(AppIdConfig* pConfig)
+{
+ PortPatternNode* tmp;
+
+ if ( pConfig->servicePortPattern)
+ {
+ while ((tmp = pConfig->servicePortPattern->luaInjectedPatterns))
+ {
+ pConfig->servicePortPattern->luaInjectedPatterns = tmp->next;
+ snort_free(tmp->pattern);
+ snort_free(tmp->detectorName);
+ snort_free(tmp);
+ }
+
+ snort_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;
+ AppIdConfig* pConfig;
+ PortPatternNode* pPattern;
+ IpProtocol protocol;
+ uint16_t port;
+ const char* pattern;
+ size_t patternSize = 0;
+ unsigned position;
+ AppId appId;
+
+ /* Verify detector user data and that we are not in packet context */
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, index++);
+
+ pConfig = ud->pAppidNewConfig;
+ protocol = (IpProtocol)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)
+ pConfig->clientPortPattern =
+ (decltype(pConfig->clientPortPattern))snort_calloc(sizeof(ClientPortPattern));
+
+ if (appId <= APP_ID_NONE || !pattern || !patternSize || (protocol != IpProtocol::TCP &&
+ protocol !=
+ IpProtocol::UDP))
+ {
+ ErrorMessage("addPortPatternClient(): Invalid input in %s\n",
+ ud->name.c_str());
+ return 0;
+ }
+ pPattern = (decltype(pPattern))snort_calloc(sizeof(PortPatternNode));
+ pPattern->pattern = (decltype(pPattern->pattern))snort_calloc(patternSize);
+ pPattern->appId = appId;
+ pPattern->protocol = protocol;
+ pPattern->port = port;
+ memcpy(pPattern->pattern, pattern, patternSize);
+ pPattern->length = patternSize;
+ pPattern->offset = position;
+ pPattern->detectorName = snort_strdup(ud->name.c_str());
+
+ //insert ports in order.
+ {
+ PortPatternNode** prev;
+ PortPatternNode** curr;
+
+ prev = nullptr;
+ 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;
+ AppIdConfig* pConfig;
+ PortPatternNode* pPattern;
+ IpProtocol protocol;
+ uint16_t port;
+ const char* pattern;
+ unsigned position;
+ AppId appId;
+
+ /* Verify detector user data and that we are not in packet context */
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, index++);
+
+ pConfig = ud->pAppidNewConfig;
+ protocol = (IpProtocol)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)
+ pConfig->servicePortPattern =
+ (decltype(pConfig->servicePortPattern))snort_calloc(sizeof(ServicePortPattern));
+
+ pPattern = (decltype(pPattern))snort_calloc(sizeof(PortPatternNode));
+ pPattern->pattern = (decltype(pPattern->pattern))snort_calloc(patternSize);
+ pPattern->appId = appId;
+ pPattern->protocol = protocol;
+ pPattern->port = port;
+ memcpy(pPattern->pattern, pattern, patternSize);
+ pPattern->length = patternSize;
+ pPattern->offset = position;
+ pPattern->detectorName = snort_strdup(ud->name.c_str());
+
+ //insert ports in order.
+ {
+ PortPatternNode** prev;
+ PortPatternNode** curr;
+
+ prev = nullptr;
+ 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 <clienAppId, clientVersion, multi-Pattern> format. */
+static int Detector_addSipServer(lua_State* L)
+{
+ int index = 1;
+
+ /* Verify detector user data and that we are not in packet context */
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, index++);
+
+ u_int32_t client_app = lua_tointeger(L, index++);
+ const char* clientVersion = lua_tostring(L, index++);
+ if (!clientVersion )
+ {
+ ErrorMessage("Invalid sip client version string.");
+ return 0;
+ }
+
+ if (ud->validateParams.pkt)
+ {
+ ErrorMessage("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)
+ {
+ ErrorMessage("Invalid sip ua pattern string.");
+ return 0;
+ }
+
+ // FIXIT - uncomment when sip detector is included in the build
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ sipServerPatternAdd(client_app, clientVersion, uaPattern,
+ &ud->pAppidNewConfig->detectorSipConfig);
+#endif
+ appInfoSetActive(client_app, true);
+
+ return 0;
+}
+
+static inline int ConvertStringToAddress(const char* string, sfip_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)
+{
+ sfip_t client_addr;
+ sfip_t server_addr;
+ IpProtocol proto;
+ uint16_t client_port, server_port;
+ char* pattern;
+ AppId service_app_id, client_app_id, payload_app_id, app_id_to_snort;
+ int16_t snort_app_id;
+ AppIdData* fp;
+
+ auto& ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ /*check inputs and whether this function is called in context of a packet */
+ if ( !ud->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 = (IpProtocol)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, pAppidActiveConfig);
+ if (nullptr == entry)
+ return 0;
+ snort_app_id = entry->snortId;
+ }
+ else
+ {
+ snort_app_id = 0;
+ }
+
+ fp = AppIdEarlySessionCreate(ud->validateParams.flowp,
+ ud->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 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 },
+ { "addDNSHostPattern", Detector_addDNSHostPattern },
+
+ /*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 },
+
+ { 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;
+
+ DebugFormat(DEBUG_APPID,"Finishing detector %s\n",detector->server.serviceModule.name);
+
+ myLuaState = detector->myLuaState;
+
+ if ( !detector->packageInfo.server.cleanFunctionName.empty() && lua_checkstack(myLuaState, 1))
+ {
+ lua_getglobal(myLuaState, detector->packageInfo.server.cleanFunctionName.c_str());
+
+ if (lua_pcall(myLuaState, 0, 0, 0))
+ {
+ /*See comment at first lua_pcall() */
+ ErrorMessage("%s: error running %s in lua: %s", detector->server.serviceModule.name,
+ detector->packageInfo.server.cleanFunctionName.c_str(), lua_tostring(myLuaState,
+ -1));
+ }
+ }
+ else if ( !detector->packageInfo.client.cleanFunctionName.empty() && lua_checkstack(myLuaState,
+ 1))
+ {
+ lua_getglobal(myLuaState, detector->packageInfo.client.cleanFunctionName.c_str());
+
+ if (lua_pcall(myLuaState, 0, 0, 0))
+ {
+ /*See comment at first lua_pcall() */
+ ErrorMessage("%s: error running %s in lua: %s", detector->server.serviceModule.name,
+ detector->packageInfo.client.cleanFunctionName.c_str(), lua_tostring(myLuaState,
+ -1));
+ }
+ }
+ else
+ {
+ ErrorMessage("%s: DetectorFini not provided\n", detector->name.c_str());
+ }
+
+ 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 UserData<Detector> 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*)
+{
+ return 0;
+}
+
+/*convert detector to string for printing */
+static int Detector_tostring(lua_State* L)
+{
+ lua_pushfstring(L, "Detector (%p)", UserData<Detector>::check(L, DETECTOR, 1));
+ return 1;
+}
+
+static const luaL_reg Detector_meta[] =
+{
+ { "__gc", Detector_gc }, // FIXIT-M J As of right now, Detector_gc is a no-op
+ { "__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, nullptr, 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)
+ snort_free(element->detectorHTTPPattern.pattern);
+ snort_free(element);
+ }
+}
+
+static void FreeCHPAppListElement(CHPListElement* element)
+{
+ if (element)
+ {
+ if (element->chp_action.pattern)
+ snort_free(element->chp_action.pattern);
+ if (element->chp_action.action_data)
+ snort_free(element->chp_action.action_data);
+ free (element);
+ }
+}
+
+static void FreeDetectorAppUrlPattern(DetectorAppUrlPattern* pattern)
+{
+ if (pattern)
+ {
+ if (pattern->userData.query.pattern)
+ snort_free(*(void**)&pattern->userData.query.pattern);
+ if (pattern->patterns.host.pattern)
+ snort_free(*(void**)&pattern->patterns.host.pattern);
+ if (pattern->patterns.path.pattern)
+ snort_free(*(void**)&pattern->patterns.path.pattern);
+ if (pattern->patterns.scheme.pattern)
+ snort_free(*(void**)&pattern->patterns.scheme.pattern);
+ // FIXIT - pattern still allocated with calloc/realloc
+ free(pattern);
+ }
+}
+
+void CleanHttpPatternLists(AppIdConfig* 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] = nullptr;
+ }
+ for (i = 0; i < pConfig->httpPatternLists.RTMPUrlList.usedCount; i++)
+ {
+ FreeDetectorAppUrlPattern(pConfig->httpPatternLists.RTMPUrlList.urlPattern[i]);
+ pConfig->httpPatternLists.RTMPUrlList.urlPattern[i] = nullptr;
+ }
+ if (pConfig->httpPatternLists.appUrlList.urlPattern)
+ {
+ snort_free(pConfig->httpPatternLists.appUrlList.urlPattern);
+ pConfig->httpPatternLists.appUrlList.urlPattern = nullptr;
+ }
+ pConfig->httpPatternLists.appUrlList.allocatedCount = 0;
+ if (pConfig->httpPatternLists.RTMPUrlList.urlPattern)
+ {
+ snort_free(pConfig->httpPatternLists.RTMPUrlList.urlPattern);
+ pConfig->httpPatternLists.RTMPUrlList.urlPattern = nullptr;
+ }
+ 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);
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Detector
+// -----------------------------------------------------------------------------
+
+Detector::~Detector()
+{
+ if ( server.pServiceElement )
+ delete server.pServiceElement;
+
+ // release the reference of the userdata on the lua side
+ if ( detectorUserDataRef != LUA_REFNIL )
+ luaL_unref(myLuaState, LUA_REGISTRYINDEX, detectorUserDataRef);
+
+ delete[] validatorBuffer;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// lua_detector_api.h author Sourcefire Inc.
+
+#ifndef LUA_DETECTOR_API_H
+#define LUA_DETECTOR_API_H
+
+// This module supports basic API towards Lua detectors.
+
+#include <cstdint>
+#include <string>
+
+#include "client_plugins/client_app_api.h"
+#include "service_plugins/service_api.h"
+
+struct Packet;
+struct ProfileStats;
+struct ServiceValidationArgs;
+struct lua_State;
+class AppIdConfig;
+class AppIdData;
+struct RNAServiceElement;
+
+struct DetectorPackageInfo
+{
+ struct UniInfo
+ {
+ std::string initFunctionName = "DetectorInit"; // client init function
+ std::string cleanFunctionName = "DetectorClean"; // client clean function
+ std::string validateFunctionName = "DetectorValidate"; // client validate function
+ int minimum_matches = 0;
+ };
+
+ std::string name = "NoName";
+ IpProtocol proto;
+
+ UniInfo client;
+ UniInfo server;
+};
+
+struct Detector
+{
+ ~Detector();
+
+ /**Identifies customer created detectors using SDL. */
+ bool isCustom;
+ bool isActive;
+ bool wasActive;
+
+ struct
+ {
+ const uint8_t* data;
+ uint16_t size;
+ int dir;
+ AppIdData* flowp;
+ Packet* pkt;
+ uint8_t macAddress[6];
+ } validateParams;
+
+ /**Pointer to flow created by a validator.
+ */
+ AppIdData* pFlow;
+
+ struct
+ {
+ unsigned int serviceId;
+
+ /**present only for server detectors*/
+ RNAServiceValidationModule serviceModule;
+
+ /**calloced buffer to satisfy internal flow API.
+ */
+ 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. */
+ RNAClientAppModule appModule;
+ } client;
+
+ lua_State* myLuaState;
+
+ /**Reference to lua userdata. This is a key into LUA_REGISTRYINDEX */
+ int detectorUserDataRef;
+
+ std::string name; // lua file name is used as detector name
+
+ /**Package information retrieved from detector lua file.
+ */
+ DetectorPackageInfo packageInfo;
+
+ unsigned detector_version;
+ char* validatorBuffer;
+ unsigned char digest[16];
+
+ AppIdConfig* pAppidActiveConfig; ///< AppId context in which this detector should be used;
+ // used during packet processing
+ AppIdConfig* pAppidOldConfig; ///< AppId context in which this detector should be
+ // cleaned; used at reload free and exit
+ AppIdConfig* pAppidNewConfig; ///< AppId context in which this detector should be
+ // loaded; used at initialization and reload
+
+ /**Snort profiling stats for individual Lua detector.*/
+ ProfileStats* pPerfStats;
+};
+
+int Detector_register(lua_State*);
+void Detector_fini(void* detector);
+void detectorRemoveAllPorts(Detector*, AppIdConfig*);
+Detector* createDetector(lua_State*, const char* filename);
+CLIENT_APP_RETCODE validateAnyClientApp(
+ const uint8_t* data,
+ uint16_t size,
+ const int dir,
+ AppIdData*,
+ Packet*,
+ Detector*,
+ const AppIdConfig*
+);
+
+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*);
+
+void CleanHttpPatternLists(AppIdConfig*);
+void CleanClientPortPatternList(AppIdConfig*);
+void CleanServicePortPatternList(AppIdConfig*);
+
+int validateAnyService(ServiceValidationArgs*);
+int checkServiceElement(Detector*);
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// lua_detector_flow_api.cc author Sourcefire Inc.
+
+#include "lua_detector_flow_api.h"
+
+#include <lua.hpp>
+
+#include "appid_api.h"
+#include "log/messages.h"
+#include "lua_detector_api.h"
+#include "lua_detector_util.h"
+#include "lua_detector_module.h"
+#include "main/snort_debug.h"
+#include "sfip/sf_ip.h"
+#include "util/common_util.h"
+
+/*static const char * LuaLogLabel = "luaDetectorFlowApi"; */
+
+#define DETECTOR "Detector"
+#define DETECTORFLOW "DetectorFlow"
+
+#if 1 // FIXIT-M hacks
+#endif
+
+/* 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 */
+ 0, /* 20 */
+ 0, /* 21 */
+ 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 */
+ 0, /* 13 */
+ 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;
+}
+
+/**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 UserData<DetectorFlow>/stack - A userdata representing UserData<DetectorFlow>.
+ */
+static int DetectorFlow_new(lua_State* L)
+{
+ sfip_t saddr;
+ sfip_t daddr;
+ IpProtocol proto;
+ uint16_t sport, dport;
+ char* pattern;
+ size_t patternLen;
+
+ auto& detector_ud = *UserData<Detector>::check(L, DETECTOR, 1);
+
+ /*check inputs and whether this function is called in context of a packet */
+ if ( !detector_ud->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 = (IpProtocol)lua_tonumber(L, 6);
+
+ auto detector_flow = new DetectorFlow();
+ UserData<DetectorFlow>::push(L, DETECTORFLOW, detector_flow);
+
+ detector_flow->myLuaState = L;
+ lua_pushvalue(L, -1);
+ detector_flow->userDataRef = luaL_ref(L, LUA_REGISTRYINDEX);
+
+ sflist_add_tail(&allocatedFlowList, detector_flow);
+
+ detector_flow->pFlow = AppIdEarlySessionCreate((AppIdData*)detector_flow,
+ detector_ud->validateParams.pkt,
+ &saddr, sport, &daddr, dport, proto, 0, 0);
+
+ if (!detector_flow->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)
+ {
+ auto L = pDetectorFlow->myLuaState;
+ luaL_unref(L, LUA_REGISTRYINDEX, pDetectorFlow->userDataRef);
+ pDetectorFlow->userDataRef = LUA_REFNIL;
+ }
+
+ delete pDetectorFlow;
+}
+
+/**Sets a flow flag.
+ *
+ * @param Lua_State* - Lua state variable.
+ * @param detectorFlow/stack - UserData<DetectorFlow> 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
+ )
+{
+ uint64_t flags;
+
+ auto& pLuaData = *UserData<DetectorFlow>::check(L, DETECTORFLOW, 1);
+ assert(pLuaData.ptr);
+
+ flags = lua_tonumber(L, 2);
+ flags = ConvertFlagsLuaToC(flags);
+
+ setAppIdFlag(pLuaData->pFlow, flags);
+
+ return 0;
+}
+
+/**Gets a flow flag value.
+ *
+ * @param Lua_State* - Lua state variable.
+ * @param detectorFlow/stack - UserData<DetectorFlow> 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
+ )
+{
+ uint64_t flags;
+ uint64_t ret;
+
+ auto& pLuaData = *UserData<DetectorFlow>::check(L, DETECTORFLOW, 1);
+ assert(pLuaData.ptr);
+
+ flags = lua_tonumber(L, 2);
+ flags = ConvertFlagsLuaToC(flags);
+
+ ret = getAppIdFlag(pLuaData->pFlow, flags);
+ ret = ConvertFlagsCToLua(ret);
+ lua_pushnumber(L, ret);
+
+ return 1;
+}
+
+/**Clear a flow flag.
+ *
+ * @param Lua_State* - Lua state variable.
+ * @param detectorFlow/stack - UserData<DetectorFlow> 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
+ )
+{
+ uint64_t flags;
+
+ auto& pLuaData = *UserData<DetectorFlow>::check(L, DETECTORFLOW, 1);
+ assert(pLuaData.ptr);
+
+ flags = lua_tonumber(L, 2);
+ flags = ConvertFlagsLuaToC(flags);
+
+ clearAppIdFlag(pLuaData->pFlow, flags);
+
+ return 0;
+}
+
+/**Set service id on a flow.
+ *
+ * @param Lua_State* - Lua state variable.
+ * @param detectorFlow/stack - UserData<DetectorFlow> 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*)
+{ return 0; }
+
+/**Set client application id on a flow.
+ *
+ * @param Lua_State* - Lua state variable.
+ * @param detectorFlow/stack - UserData<DetectorFlow> 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_setFlowClnAppId(
+ lua_State*
+ )
+{
+ return 0;
+}
+
+/**Set client application type id on a flow.
+ *
+ * @param Lua_State* - Lua state variable.
+ * @param detectorFlow/stack - UserData<DetectorFlow> 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_setFlowClnAppType(
+ lua_State*
+ )
+{
+ 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 UserData<DetectorFlow> object.
+ *
+ * @param Lua_State* - Lua state variable.
+ * @param detectorflow/stack - UserData<DetectorFlow> 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
+ )
+{
+ auto& pLuaData = *UserData<DetectorFlow>::check(L, DETECTORFLOW, 1);
+ assert(pLuaData.ptr);
+
+ lua_pushlstring(L, (char*)&pLuaData->pFlow->id,
+ sizeof(pLuaData->pFlow->id));
+
+ 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 },
+ { "setFlowClnAppId", DetectorFlow_setFlowClnAppId },
+ { "setFlowClnAppType", DetectorFlow_setFlowClnAppType },
+ { "getFlowKey", DetectorFlow_getFlowKey },
+ { 0, 0 }
+};
+
+/**
+ * lua_close will ensure that all detectors and flows get _gc called.
+ */
+static int DetectorFlow_gc(
+ lua_State*
+ )
+{
+ return 0;
+}
+
+static int DetectorFlow_tostring(
+ lua_State* L
+ )
+{
+ char buff[32];
+ snprintf(buff, sizeof(buff), "%p", (void*)UserData<DetectorFlow>::check(L, DETECTORFLOW, 1));
+ lua_pushfstring(L, "UserData<DetectorFlow> (%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 - UserData<DetectorFlow> 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, nullptr, 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 */
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// lua_detector_flow_api.h author Sourcefire Inc.
+
+#ifndef LUA_DETECTOR_FLOW_API_H
+#define LUA_DETECTOR_FLOW_API_H
+
+// This module supports API towards Lua detectors for performing specific operations on a flow object.
+// The flow object on Lua side is a userData.
+
+struct lua_State;
+class AppIdData;
+
+struct DetectorFlow
+{
+ // FIXIT-H J why is the lua state and user data ref on this object?
+ lua_State* myLuaState;
+ AppIdData* pFlow;
+ int userDataRef;
+};
+
+int DetectorFlow_register(lua_State*);
+void freeDetectorFlow(void* userdata);
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// lua_detector_module.cc author Sourcefire Inc.
+
+// supporting Lua detectors in core engine.
+
+#include "lua_detector_module.h"
+
+#include <list>
+#include <algorithm>
+#include <glob.h>
+#include <lua.hpp>
+#include <openssl/md5.h>
+
+#include "appid_config.h"
+#include "client_plugins/client_app_base.h"
+#include "fw_appid.h" // for lua*PerfStats
+#include "hash/sfghash.h"
+#include "log/messages.h"
+#include "lua/lua.h"
+#include "lua_detector_api.h"
+#include "lua_detector_flow_api.h"
+#include "main/snort_debug.h"
+#include "utils/util.h"
+
+#define MD5CONTEXT MD5_CTX
+
+#define MD5INIT MD5_Init
+#define MD5UPDATE MD5_Update
+#define MD5FINAL MD5_Final
+#define MD5DIGEST MD5
+
+#define MAXPD 1024
+#define LUA_DETECTOR_FILENAME_MAX 1024
+
+// 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 std::list<Detector*> allocatedDetectorList;
+
+SF_LIST allocatedFlowList; /*list of flows allocated. */
+static uint32_t gLuaTrackerSize = 0;
+static unsigned gNumDetectors = 0;
+static unsigned gNumActiveDetectors;
+
+inline bool match_char_set(char c, const char* set)
+{
+ while ( *set && *set != c )
+ ++set;
+
+ return *set != '\0';
+}
+
+inline const char* find_first_not_of(const char* s, const char* const set)
+{
+ while ( *s && match_char_set(*s, set) )
+ ++s;
+
+ return s;
+}
+
+inline const char* find_first_of(const char* s, const char* const set)
+{
+ while ( *s && !match_char_set(*s, set) )
+ ++s;
+
+ return s;
+}
+
+const char* tokenize(const char* const delim, const char*& save, size_t& len)
+{
+ if ( !save || !*save )
+ return nullptr;
+
+ save = find_first_not_of(save, delim);
+
+ if ( !*save )
+ return nullptr;
+
+ const char* end = find_first_of(save, delim);
+
+ const char* tmp = save;
+
+ len = end - save;
+ save = end;
+
+ return tmp;
+}
+
+static inline bool get_lua_ns(lua_State* L, const char* const ns)
+{
+ const char* save = ns;
+ size_t len = 0;
+
+ lua_pushvalue(L, LUA_GLOBALSINDEX);
+
+ while ( const char* s = tokenize(". ", save, len) )
+ {
+ if ( !lua_istable(L, -1) )
+ return false;
+
+ lua_pushlstring(L, s, len);
+ lua_gettable(L, -2);
+ }
+
+ return true;
+}
+
+static inline bool get_lua_field(
+ lua_State* L, int table, const char* field, std::string& out)
+{
+ lua_getfield(L, table, field);
+ bool result = lua_isstring(L, -1);
+ if ( result )
+ out = lua_tostring(L, -1);
+
+ lua_pop(L, 1);
+ return result;
+}
+
+static inline bool get_lua_field(
+ lua_State* L, int table, const char* field, int& out)
+{
+ lua_getfield(L, table, field);
+ bool result = lua_isnumber(L, -1);
+ if ( result )
+ out = lua_tointeger(L, -1);
+
+ lua_pop(L, 1);
+ return result;
+}
+
+static inline bool get_lua_field(
+ lua_State* L, int table, const char* field, IpProtocol& out)
+{
+ lua_getfield(L, table, field);
+ bool result = lua_isnumber(L, -1);
+ if ( result )
+ out = (IpProtocol)lua_tointeger(L, -1);
+
+ lua_pop(L, 1);
+ return result;
+}
+
+static lua_State* createLuaState()
+{
+ // FIXIT-H J should obtain lua states from lua state factory
+ auto L = luaL_newstate();
+ luaL_openlibs(L);
+
+// FIXIT-M J this is stupid, remove it
+#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(L);
+ // After detector register the methods are still on the stack, remove them
+ lua_pop(L, 1);
+
+ DetectorFlow_register(L);
+ lua_pop(L, 1);
+
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ /*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);
+#endif
+
+ // set lua library paths
+ char extra_path_buffer[PATH_MAX];
+
+ snprintf(
+ extra_path_buffer, PATH_MAX-1, "%s/odp/libs/?.lua;%s/custom/libs/?.lua",
+ pAppidActiveConfig->mod_config->app_detector_dir,
+ pAppidActiveConfig->mod_config->app_detector_dir);
+
+ const int save_top = lua_gettop(L);
+ if ( get_lua_ns(L, "package.path") )
+ {
+ lua_pushstring(L, extra_path_buffer);
+ lua_concat(L, 2);
+ lua_setfield(L, -2, "path");
+ }
+ else
+ ErrorMessage("Could not set lua package.path\n");
+
+ lua_settop(L, save_top);
+
+ return L;
+}
+
+#ifdef REMOVED_WHILE_NOT_IN_USE
+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 = snort_strdup("NoName");
+ pkg->server.initFunctionName = snort_strdup("DetectorInit");
+ pkg->server.cleanFunctionName = snort_strdup("DetectorClean");
+ pkg->server.validateFunctionName = snort_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 = snort_strdup(lua_tostring(L, -1));
+ if (!pkg->name)
+ _dpd.errMsg("failed to allocate package name");
+ }
+ else if (fillDefaults)
+ {
+ pkg->name = snort_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 = snort_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 = snort_strdup(lua_tostring(L, -1));
+ if (!pkg->client.cleanFunctionName)
+ lua_getfield(L, -1, "validate"); /* string*/
+ if (lua_isstring(L, -1))
+ {
+ pkg->client.validateFunctionName = snort_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 = snort_strdup(lua_tostring(L, -1));
+ if (!pkg->server.initFunctionName)
+ _dpd.errMsg("failed to allocate server init function name");
+ }
+ else if (fillDefaults)
+ {
+ pkg->server.initFunctionName = snort_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 = snort_strdup(lua_tostring(L, -1));
+ if (!pkg->server.cleanFunctionName)
+ _dpd.errMsg("failed to allocate server clean function name");
+ }
+ else if (fillDefaults)
+ {
+ pkg->server.cleanFunctionName = snort_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 = snort_strdup(lua_tostring(L, -1));
+ if (!pkg->server.validateFunctionName)
+ _dpd.errMsg("failed to allocate server validate function name");
+ }
+ else if (fillDefaults)
+ {
+ pkg->server.validateFunctionName = snort_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 */
+ }
+}
+
+#endif
+
+// fetch or create packageInfo defined inside lua detector
+static void getDetectorPackageInfo(Detector* detector)
+{
+ auto L = detector->myLuaState;
+ Lua::ManageStack mgr(L);
+
+ auto& pkg = detector->packageInfo;
+ lua_getglobal(L, "DetectorPackageInfo");
+
+ // use defaults
+ if ( lua_isnil(L, -1) )
+ return;
+
+ // get name
+ get_lua_field(L, -1, "name", pkg.name);
+
+ // get proto
+ if ( !get_lua_field(L, -1, "proto", pkg.proto) )
+ {
+ // FIXIT-M J error messages should use source info
+ ErrorMessage("DetectorPackageInfo field 'proto' is not a number\n");
+ }
+
+ // get client
+ lua_getfield(L, -1, "client");
+ if ( !lua_istable(L, -1) )
+ {
+ // FIXIT-M J error messages should use source info
+ ErrorMessage("DetectorPackageInfo field 'client' is not a table\n");
+ }
+ else
+ {
+ get_lua_field(L, -1, "init", pkg.client.initFunctionName);
+ get_lua_field(L, -1, "clean", pkg.client.cleanFunctionName);
+ get_lua_field(L, -1, "validate", pkg.client.validateFunctionName);
+ get_lua_field(L, -1, "minimum_matches", pkg.client.minimum_matches);
+ }
+
+ // pop client table
+ lua_pop(L, 1);
+
+ // get server
+ lua_getfield(L, -1, "server");
+ if ( !lua_istable(L, -1) )
+ {
+ // FIXIT-M J error messages should use source info
+ ErrorMessage("DetectorPackageInfo field 'server' is not a table\n");
+ }
+ else
+ {
+ get_lua_field(L, -1, "init", pkg.server.initFunctionName);
+ get_lua_field(L, -1, "clean", pkg.server.cleanFunctionName);
+ get_lua_field(L, -1, "validate", pkg.server.validateFunctionName);
+ get_lua_field(L, -1, "minimum_matches", pkg.server.minimum_matches);
+ }
+}
+
+/**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(Detector* detector)
+{
+ const auto& name = detector->name;
+ auto L = detector->myLuaState;
+ const auto& server = detector->packageInfo.server;
+
+ if ( server.initFunctionName.empty() )
+ {
+ ErrorMessage("Detector %s: DetectorInit() is not provided for server\n", name.c_str());
+ return;
+ }
+
+ lua_getglobal(L, server.initFunctionName.c_str());
+
+ if (!lua_isfunction(L, -1))
+ {
+ ErrorMessage("Detector %s: does not contain DetectorInit() function\n", name.c_str());
+ return;
+ }
+
+ /*first parameter is DetectorUserData */
+ lua_rawgeti(L, LUA_REGISTRYINDEX, detector->detectorUserDataRef);
+
+ if ( lua_pcall(L, 1, 1, 0) )
+ {
+ ErrorMessage("error loading lua Detector %s, error %s\n",
+ name.c_str(), lua_tostring(L, -1));
+ return;
+ }
+ else
+ {
+ if ( detector->server.pServiceElement )
+ detector->server.pServiceElement->ref_count = 1;
+
+ DebugFormat(DEBUG_APPID, "Initialized %s\n", name.c_str());
+ }
+}
+
+/**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(Detector* detector)
+{
+ auto L = detector->myLuaState;
+ const auto& client = detector->packageInfo.client;
+
+ assert(!client.initFunctionName.empty());
+
+ lua_getglobal(L, client.initFunctionName.c_str());
+ if (!lua_isfunction(L, -1))
+ {
+ ErrorMessage("Detector %s: does not contain DetectorInit() function\n",
+ detector->name.c_str());
+ return;
+ }
+
+ /*first parameter is DetectorUserData */
+ lua_rawgeti(L, LUA_REGISTRYINDEX, detector->detectorUserDataRef);
+
+ /*second parameter is a table containing configuration stuff. */
+ // ... which is empty.???
+ lua_newtable(L);
+
+ if ( lua_pcall(L, 2, 1, 0) )
+ {
+ // FIXIT shouldn't this be using detector->name?
+ ErrorMessage("Could not initialize the %s client app element: %s\n",
+ detector->name.c_str(), lua_tostring(L, -1));
+ return;
+ }
+ else
+ {
+ DebugFormat(DEBUG_APPID, "Initialized %s\n", detector->name.c_str());
+ }
+}
+
+static void luaClientFini(Detector* detector)
+{
+ auto L = detector->myLuaState;
+ const auto& client = detector->packageInfo.client;
+
+ assert(!client.cleanFunctionName.empty() );
+
+ lua_getglobal(L, client.cleanFunctionName.c_str());
+ if (!lua_isfunction(L, -1))
+ {
+ ErrorMessage("Detector %s: does not contain DetectorFini() function\n",
+ detector->name.c_str());
+ return;
+ }
+
+ /*first parameter is DetectorUserData */
+ lua_rawgeti(L, LUA_REGISTRYINDEX, detector->detectorUserDataRef);
+
+ if ( lua_pcall(L, 1, 1, 0) )
+ {
+ ErrorMessage("Could not cleanup the %s client app element: %s\n",
+ detector->name.c_str(), lua_tostring(L, -1));
+ }
+}
+
+/**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, "hosServiceTrackerModule");
+ if (lua_istable(L, -1))
+ {
+ lua_getfield(L, -1, "setHosServiceTrackerSize");
+ if (lua_isfunction(L, -1))
+ {
+ lua_pushinteger (L, numTrackers);
+ if (lua_pcall(L, 1, 0, 0) != 0)
+ {
+ ErrorMessage("error setting tracker size");
+ }
+ }
+ }
+ else
+ {
+#ifdef LUA_DETECTOR_DEBUG
+ DebugFormat(DEBUG_LOG, "hosServiceTrackerModule.setHosServiceTrackerSize 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)
+ {
+ ErrorMessage("error setting tracker size");
+ }
+ }
+ }
+ else
+ {
+#ifdef LUA_DETECTOR_DEBUG
+ DebugFormat(DEBUG_LOG, "flowTrackerModule.setFlowTrackerSize not found");
+#endif
+ }
+ lua_pop(L, 1);
+}
+
+static void luaCustomLoad( char* detectorName, char* validator, unsigned int validatorLen,
+ unsigned char* const digest, AppIdConfig* pConfig, bool isCustom)
+{
+ Detector* detector;
+ RNAClientAppModule* cam = nullptr;
+
+ lua_State* L = createLuaState();
+ if ( !L )
+ {
+ ErrorMessage("can not create new luaState");
+ snort_free(validator);
+ return;
+ }
+
+ if ( luaL_loadbuffer(L, validator, validatorLen, "<buffer>") ||
+ lua_pcall(L, 0, 0, 0) )
+ {
+ ErrorMessage("cannot run validator %s, error: %s\n",
+ detectorName, lua_tostring(L, -1));
+
+ lua_close(L);
+ snort_free(validator);
+
+ return;
+ }
+
+ detector = createDetector(L, detectorName);
+ if ( !detector )
+ {
+ ErrorMessage("cannot allocate detector %s\n", detectorName);
+ lua_close(L);
+ snort_free(validator);
+
+ return;
+ }
+
+ getDetectorPackageInfo(detector);
+ detector->validatorBuffer = validator;
+ detector->isActive = true;
+ detector->pAppidNewConfig = detector->pAppidActiveConfig = detector->pAppidOldConfig = pConfig;
+ detector->isCustom = isCustom;
+
+ if ( detector->packageInfo.server.initFunctionName.empty() )
+ {
+ assert(false); // FIXIT-H J cam is null at this point so... WOMP
+ detector->client.appFpId = APP_ID_UNKNOWN;
+ cam = &detector->client.appModule;
+ // cam->name = detector->packageInfo.name;
+ cam->proto = detector->packageInfo.proto;
+ cam->validate = validateAnyClientApp;
+ cam->minimum_matches = detector->packageInfo.client.minimum_matches;
+ cam->userData = detector;
+ cam->api = getClientApi();
+ }
+ else
+ {
+ /*add to active service list */
+ detector->server.serviceModule.next = pConfig->serviceConfig.active_service_list;
+ pConfig->serviceConfig.active_service_list = &detector->server.serviceModule;
+
+ detector->server.serviceId = APP_ID_UNKNOWN;
+
+ /*create a ServiceElement */
+ if (checkServiceElement(detector))
+ {
+ detector->server.pServiceElement->validate = validateAnyService;
+ detector->server.pServiceElement->userdata = detector;
+
+ detector->server.pServiceElement->detectorType = DETECTOR_TYPE_DECODER;
+ }
+ }
+
+ memcpy(detector->digest, digest, sizeof(detector->digest));
+ allocatedDetectorList.push_front(detector);
+ gNumDetectors++;
+
+ DebugFormat(DEBUG_LOG,"Loaded detector %s\n", detectorName);
+}
+
+void LuaDetectorModuleManager::luaModuleInit()
+{
+ sflist_init(&allocatedFlowList);
+ allocatedDetectorList.clear();
+}
+
+/**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(char* path, AppIdConfig* pConfig, bool isCustom)
+{
+ unsigned n;
+ FILE* file;
+ char pattern[PATH_MAX];
+ snprintf(pattern, sizeof(pattern), "%s/*", path);
+
+ glob_t globs;
+ memset(&globs, 0, sizeof(globs));
+ int rval = glob(pattern, 0, nullptr, &globs);
+ if (rval != 0 && rval != GLOB_NOMATCH)
+ {
+ ErrorMessage("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")) == nullptr)
+ {
+ ErrorMessage("Unable to read lua detector '%s'\n",globs.gl_pathv[n]);
+ continue;
+ }
+
+ /*Load lua file as a detector. */
+ if (fseek(file, 0, SEEK_END))
+ {
+ ErrorMessage("Unable to seek lua detector '%s'\n",globs.gl_pathv[n]);
+ continue;
+ }
+
+ auto validatorBufferLen = ftell(file);
+
+ if (validatorBufferLen == -1)
+ {
+ ErrorMessage("Unable to return offset on lua detector '%s'\n",globs.gl_pathv[n]);
+ continue;
+ }
+ if (fseek(file, 0, SEEK_SET))
+ {
+ ErrorMessage("Unable to seek lua detector '%s'\n",globs.gl_pathv[n]);
+ continue;
+ }
+
+ auto validatorBuffer = new uint8_t[validatorBufferLen + 1]();
+
+ if (fread(validatorBuffer, validatorBufferLen, 1, file) == 0)
+ {
+ ErrorMessage("Failed to read lua detector %s\n",globs.gl_pathv[n]);
+ delete[] validatorBuffer;
+ continue;
+ }
+
+ validatorBuffer[validatorBufferLen] = '\0';
+
+ MD5INIT(&context);
+ MD5UPDATE(&context, validatorBuffer, validatorBufferLen);
+ MD5FINAL(digest, &context);
+
+ // FIXIT-H J this finds the wrong detector -- it should be find_last_of
+ auto it = std::find_if(
+ allocatedDetectorList.begin(),
+ allocatedDetectorList.end(),
+ [&detectorName](const Detector* d) {
+ return d->name == detectorName;
+ });
+
+ if ( it != allocatedDetectorList.end() )
+ {
+ Detector* detector = *it;
+ if ( !memcmp(digest, detector->digest, sizeof(digest)) )
+ {
+ detector->isActive = true;
+ detector->pAppidNewConfig = pConfig;
+ delete[] validatorBuffer;
+ }
+ }
+
+ luaCustomLoad(detectorName, (char*)validatorBuffer, validatorBufferLen, digest, pConfig,
+ isCustom);
+ }
+
+ globfree(&globs);
+}
+
+void LuaDetectorModuleManager::FinalizeLuaModules(AppIdConfig* pConfig)
+{
+ gNumActiveDetectors = 0;
+
+ for ( auto& detector : allocatedDetectorList )
+ {
+ 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 LuaDetectorModuleManager::LoadLuaModules(AppIdConfig* pConfig)
+{
+ for ( auto& detector : allocatedDetectorList )
+ {
+ detector->wasActive = detector->isActive;
+ detector->isActive = 0;
+
+ if ( detector->server.pServiceElement )
+ detector->server.pServiceElement->ref_count = 0;
+ }
+
+ char path[PATH_MAX];
+
+ snprintf(path, sizeof(path), "%s/odp/lua", pAppidActiveConfig->mod_config->app_detector_dir);
+ loadCustomLuaModules(path, pConfig, 0);
+ snprintf(path, sizeof(path), "%s/custom/lua",
+ pAppidActiveConfig->mod_config->app_detector_dir);
+ loadCustomLuaModules(path, pConfig, 1);
+ // luaDetectorsCleanInactive();
+}
+
+void luaDetectorsUnload(AppIdConfig* pConfig)
+{
+ for ( auto& detector : allocatedDetectorList )
+ {
+ if ( detector->isActive && !detector->packageInfo.server.initFunctionName.empty())
+ detectorRemoveAllPorts(detector, pConfig);
+
+ if ( detector->isActive && !detector->packageInfo.client.initFunctionName.empty() )
+ luaClientFini(detector);
+
+ detector->isActive = false;
+
+ if (detector->server.pServiceElement)
+ detector->server.pServiceElement->ref_count = 0;
+ }
+
+ gNumActiveDetectors = 0;
+}
+
+void luaDetectorsSetTrackerSize()
+{
+ gLuaTrackerSize = calculateLuaTrackerSize(512*1024*1024, gNumActiveDetectors);
+
+ DebugFormat(DEBUG_APPID, " Setting tracker size to %u\n", gLuaTrackerSize);
+
+ for ( auto& detector : allocatedDetectorList )
+ {
+ if ( detector->isActive )
+ setLuaTrackerSize(detector->myLuaState, gLuaTrackerSize);
+ }
+}
+
+void LuaDetectorModuleManager::UnloadLuaModules(AppIdConfig*)
+{
+ for ( auto& detector : allocatedDetectorList )
+ {
+ if ( detector->wasActive )
+ {
+ if ( detector->client.appFpId )
+ luaClientFini(detector);
+
+ detector->wasActive = false;
+ }
+
+ // 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()
+{
+ for ( auto& detector : allocatedDetectorList )
+ luaServerInit(detector);
+}
+
+/**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()
+{
+ for ( auto& detector : allocatedDetectorList )
+ if ( detector->isActive && !detector->packageInfo.client.initFunctionName.empty() )
+ luaClientInit(detector);
+}
+
+void luaModuleCleanAllClients()
+{
+ for ( auto& detector : allocatedDetectorList )
+ if ( !detector->packageInfo.client.initFunctionName.empty() )
+ 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 LuaDetectorModuleManager::luaModuleFini()
+{
+ DebugMessage(DEBUG_APPID, "luaModuleFini(): entered");
+
+ /*flow can be freed during garbage collection */
+
+ sflist_static_free_all(&allocatedFlowList, freeDetectorFlow);
+ allocatedDetectorList.clear();
+}
+
+void RNAPndDumpLuaStats()
+{
+ size_t totalMem = 0;
+ size_t mem;
+
+ if ( allocatedDetectorList.empty() )
+ return;
+
+ LogMessage("Lua detector Stats");
+
+ for ( auto& detector : allocatedDetectorList )
+ {
+ mem = lua_gc(detector->myLuaState, LUA_GCCOUNT, 0);
+ totalMem += mem;
+ LogMessage(" Detector %s: Lua Memory usage %zu kb", detector->name.c_str(), mem);
+ }
+
+ LogMessage("Lua Stats total memory usage %zu kb", totalMem);
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// lua_detector_module.h author Sourcefire Inc.
+
+#ifndef LUA_DETECTOR_MODULE_H
+#define LUA_DETECTOR_MODULE_H
+
+#include <string>
+
+#include "utils/sflsq.h"
+
+class AppIdConfig;
+
+class LuaDetectorModuleManager
+{
+public:
+ // Initializes Lua modules. Open lua and if available LuaJIT libraries, and registers all API modules.
+ static void luaModuleInit();
+ static void luaModuleFini();
+
+ // Load all Lua modules into a detector list
+ //
+ // Each RNA detector file in the folder 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 allocatedDetectorList.
+ static void LoadLuaModules(AppIdConfig*);
+
+ // Finalize Lua modules
+ // This function should be called after LoadLuaModules(). It sets up proper AppId references
+ // and tracker size for all the detectors.
+ static void FinalizeLuaModules(AppIdConfig*);
+
+ // 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().
+ static void UnloadLuaModules(AppIdConfig*);
+
+ static void add_chunk(const std::string&);
+};
+
+void luaModuleInitAllServices();
+void luaModuleCleanAllClients();
+void luaModuleInitAllClients();
+void RNAPndDumpLuaStats();
+
+void luaDetectorsUnload(AppIdConfig*);
+void luaDetectorsSetTrackerSize();
+
+extern SF_LIST allocatedFlowList;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// lua_detector_util.h author Joel Cornett <jocornet@cisco.com>
+
+#ifndef LUA_DETECTOR_UTIL_H
+#define LUA_DETECTOR_UTIL_H
+
+// encapsulate Lua interface boilerplate to get sane, identical behavior across users
+
+#include <cassert>
+
+#include <lua.hpp>
+
+template<typename T>
+struct UserData
+{
+ T* ptr;
+
+ T* operator->()
+ { return ptr; }
+
+ const T* operator->() const
+ { return ptr; }
+
+ operator T*() const
+ { return ptr; }
+
+ operator T*()
+ { return ptr; }
+
+ static UserData<T>* push(lua_State* L, const char* const meta, T* ptr = nullptr)
+ {
+ auto ud = static_cast<UserData<T>*>(lua_newuserdata(L, sizeof(UserData<T>)));
+ assert(ud);
+
+ ud->ptr = ptr;
+
+ luaL_getmetatable(L, meta);
+ // metatable should already be in registry at this point
+ assert(lua_istable(L, -1));
+
+ lua_setmetatable(L, -2);
+ return ud;
+ }
+
+ static UserData<T>* check(lua_State* L, const char* const meta, int n)
+ {
+ luaL_checktype(L, n, LUA_TUSERDATA);
+ auto ud = static_cast<UserData<T>*>(luaL_checkudata(L, n, meta));
+ assert(ud);
+ return ud;
+ }
+};
+
+#endif
--- /dev/null
+
+set ( SERVICE_PLUGINS_SOURCES
+ dcerpc.cc
+ dcerpc.h
+ service_api.h
+ service_base.cc
+ service_base.h
+ service_battle_field.cc
+ service_battle_field.h
+ service_bgp.cc
+ service_bgp.h
+ service_bit.cc
+ service_bootp.cc
+ service_bootp.h
+ service_config.h
+ service_dcerpc.cc
+ service_dcerpc.h
+ service_direct_connect.cc
+ service_direct_connect.h
+ service_flap.cc
+ service_flap.h
+ service_ftp.cc
+ service_ftp.h
+ service_irc.cc
+ service_irc.h
+ service_lpr.cc
+ service_lpr.h
+ service_mdns.cc
+ service_mdns.h
+ service_mysql.cc
+ service_mysql.h
+ service_netbios.cc
+ service_netbios.h
+ service_nntp.cc
+ service_nntp.h
+ service_ntp.cc
+ service_ntp.h
+ service_radius.cc
+ service_radius.h
+ service_rexec.cc
+ service_rexec.h
+ service_rfb.cc
+ service_rfb.h
+ service_rlogin.cc
+ service_rlogin.h
+ service_rpc.cc
+ service_rpc.h
+ service_rshell.cc
+ service_rshell.h
+ service_rsync.cc
+ service_rsync.h
+ service_rtmp.cc
+ service_rtmp.h
+ service_smtp.cc
+ service_smtp.h
+ service_snmp.cc
+ service_snmp.h
+ service_ssh.cc
+ service_ssh.h
+ service_ssl.cc
+ service_ssl.h
+ service_telnet.cc
+ service_telnet.h
+ service_tftp.cc
+ service_tftp.h
+ service_timbuktu.cc
+ service_tns.cc
+ service_util.h
+ )
+
+add_library ( appid_service_plugins STATIC
+ ${SERVICE_PLUGINS_SOURCES}
+)
+
+target_include_directories ( appid_service_plugins PRIVATE ${APPID_INCLUDE_DIR} )
+
+# FIXIT-H: Add unit tests
+
+#install (FILES ${SERVICE_PLUGINS_INCLUDES}
+# DESTINATION "${INCLUDE_INSTALL_PATH}/appid/service_plugins"
+#)
--- /dev/null
+
+AM_CPPFLAGS+=-I$(top_srcdir)/src/network_inspectors/appid
+
+file_list = \
+dcerpc.cc \
+dcerpc.h \
+service_api.h \
+service_base.cc \
+service_base.h \
+service_battle_field.cc \
+service_battle_field.h \
+service_bgp.cc \
+service_bgp.h \
+service_bit.cc \
+service_bootp.cc \
+service_bootp.h \
+service_config.h \
+service_dcerpc.cc \
+service_dcerpc.h \
+service_direct_connect.cc \
+service_direct_connect.h \
+service_flap.cc \
+service_flap.h \
+service_ftp.cc \
+service_ftp.h \
+service_irc.cc \
+service_irc.h \
+service_lpr.cc \
+service_lpr.h \
+service_mdns.cc \
+service_mdns.h \
+service_mysql.cc \
+service_mysql.h \
+service_netbios.cc \
+service_netbios.h \
+service_nntp.cc \
+service_nntp.h \
+service_ntp.cc \
+service_ntp.h \
+service_radius.cc \
+service_radius.h \
+service_rexec.cc \
+service_rexec.h \
+service_rfb.cc \
+service_rfb.h \
+service_rlogin.cc \
+service_rlogin.h \
+service_rpc.cc \
+service_rpc.h \
+service_rshell.cc \
+service_rshell.h \
+service_rsync.cc \
+service_rsync.h \
+service_rtmp.cc \
+service_rtmp.h \
+service_smtp.cc \
+service_smtp.h \
+service_snmp.cc \
+service_snmp.h \
+service_ssh.cc \
+service_ssh.h \
+service_ssl.cc \
+service_ssl.h \
+service_telnet.cc \
+service_telnet.h \
+service_tftp.cc \
+service_tftp.h \
+service_timbuktu.cc \
+service_tns.cc \
+service_util.h
+
+noinst_LIBRARIES = libappid_service_plugins.a
+libappid_service_plugins_a_SOURCES = $(file_list)
+
+# Uncomment once tests are ready.
+#if BUILD_UNIT_TESTS
+#SUBDIRS = test
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// dcerpc.cc author Sourcefire Inc.
+
+#include "dcerpc.h"
+
+#include <netinet/in.h>
+
+#define min(x,y) ((x)<(y) ? (x) : (y))
+
+#define DCERPC_LE_FLAG 0x10
+
+#pragma pack(1)
+
+struct DCERPCHeader
+{
+ 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;
+};
+
+#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;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// dcerpc.h author Sourcefire Inc.
+
+#ifndef DCERPC_H
+#define DCERPC_H
+
+#include <stdint.h>
+
+int dcerpc_validate(const uint8_t* data, int size);
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_api.h author Sourcefire Inc.
+
+#ifndef SERVICE_API_H
+#define SERVICE_API_H
+
+#include "appid_flow_data.h"
+
+class AppIdConfig;
+class AppIdData;
+struct Detector;
+struct RNAServiceSubtype;
+struct Packet;
+struct DynamicPreprocessorData;
+
+enum SERVICE_RETCODE
+{
+ 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
+};
+
+struct ServiceValidationArgs
+{
+ const uint8_t* data;
+ uint16_t size;
+ int dir;
+ AppIdData* flowp;
+ Packet* pkt;
+ struct Detector* userdata;
+ const AppIdConfig* pConfig;
+ bool app_id_debug_session_flag;
+ char* app_id_debug_session;
+};
+
+using RNAServiceValidationFCN = int(*)(ServiceValidationArgs*);
+
+#define MakeRNAServiceValidationPrototype(name) static int name(ServiceValidationArgs* args)
+
+struct CleanServiceAPI
+{
+ AppIdConfig* pAppidConfig; ///< AppId context for which this API should be used
+};
+
+struct IniServiceAPI;
+using RNAServiceValidationInitFCN = int(*)(const IniServiceAPI* const);
+using RNAServiceValidationCleanFCN = void(*)(const CleanServiceAPI* const);
+
+struct RNAServiceValidationPort;
+struct RNAServiceValidationModule;
+struct IniServiceAPI
+{
+ void (* RegisterPattern)(
+ RNAServiceValidationFCN, IpProtocol proto, const uint8_t* pattern,
+ unsigned size, int position, const char* name, AppIdConfig*);
+
+ int (* AddPort)(
+ RNAServiceValidationPort*, RNAServiceValidationModule*, AppIdConfig*);
+
+ void (* RemovePorts)(RNAServiceValidationFCN, AppIdConfig*);
+ void (* RegisterPatternUser)(
+ RNAServiceValidationFCN, IpProtocol proto, const uint8_t* pattern,
+ unsigned size, int position, const char* name, AppIdConfig*);
+
+ void (* RegisterAppId)(
+ RNAServiceValidationFCN, AppId, uint32_t additionalInfo, AppIdConfig*);
+
+ int debug;
+ uint32_t instance_id;
+ AppIdConfig* pAppidConfig; ///< AppId context for which this API should be used
+};
+
+struct RNAServicePerf
+{
+ /*time to validate */
+ uint64_t totalValidateTime;
+};
+
+struct RNAServiceElement
+{
+ RNAServiceElement* next;
+ RNAServiceValidationFCN validate;
+ // Value of userdata pointer and validate pointer forms key for comparison.
+ 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 void*(* ServiceFlowdataGet)(AppIdData*, unsigned);
+typedef int (* ServiceFlowdataAdd)(AppIdData*, void*, unsigned, AppIdFreeFCN);
+typedef int (* ServiceFlowdataAddId)(AppIdData*, uint16_t, const RNAServiceElement* const);
+typedef int (* ServiceFlowdataAddDHCP)(AppIdData*, unsigned, const uint8_t*, unsigned, const
+ uint8_t*, const uint8_t*);
+#define APPID_EARLY_SESSION_FLAG_FW_RULE 1
+typedef AppIdData*(* ServiceCreateNewFlow)(AppIdData* flowp, const Packet*, const sfip_t*, uint16_t,
+ const sfip_t*, uint16_t, IpProtocol, int16_t, int flags);
+typedef void (* ServiceDhcpNewLease)(AppIdData* flow, const uint8_t* mac, uint32_t ip, int32_t
+ zone, uint32_t subnetmask, uint32_t leaseSecs, uint32_t router);
+typedef void (* ServiceAnalyzeFP)(AppIdData*, unsigned, unsigned, uint32_t);
+
+typedef int (* AddService)(AppIdData* flow, const Packet* pkt, int dir,
+ const RNAServiceElement* svc_element, AppId service, const char* vendor,
+ const char* version, const RNAServiceSubtype* subtype);
+typedef int (* AddServiceConsumeSubtype)(AppIdData* flow, const Packet* pkt, int dir,
+ const RNAServiceElement* svc_element, AppId service, const char* vendor, const char* version,
+ RNAServiceSubtype* subtype);
+typedef int (* ServiceInProcess)(AppIdData* flow, const Packet* pkt, int dir,
+ const RNAServiceElement* svc_element);
+typedef int (* FailService)(AppIdData* flow, const Packet* pkt, int dir,
+ const RNAServiceElement* svc_element, unsigned flow_data_index, const AppIdConfig* pConfig);
+typedef int (* IncompatibleData)(AppIdData* flow, const Packet* pkt, int dir,
+ const RNAServiceElement* svc_element, unsigned flow_data_index, const AppIdConfig*);
+typedef void (* AddHostInfo)(AppIdData* flow, SERVICE_HOST_INFO_CODE code, const void* info);
+typedef void (* AddPayload)(AppIdData*, AppId);
+typedef void (* AddUser)(AppIdData*, const char*, AppId, int);
+typedef void (* AddMisc)(AppIdData*, AppId);
+typedef void (* AddDnsQueryInfo)(AppIdData* flow, uint16_t id, const uint8_t* host,
+ uint8_t host_len, uint16_t host_offset, uint16_t record_type);
+typedef void (* AddDnsResponseInfo)(AppIdData* 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)(AppIdData* flow);
+
+struct ServiceApi
+{
+ 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;
+ 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;
+};
+
+struct RNAFlowState
+{
+ RNAFlowState* next;
+ const RNAServiceElement* svc;
+ uint16_t port;
+};
+
+struct RNAServiceValidationPort
+{
+ RNAServiceValidationFCN validate;
+ uint16_t port;
+ IpProtocol proto;
+ uint8_t reversed_validation;
+};
+
+struct RNAServiceValidationModule
+{
+ const char* name;
+ RNAServiceValidationInitFCN init;
+ RNAServiceValidationPort* pp;
+ const ServiceApi* api;
+ RNAServiceValidationModule* next;
+ int provides_user;
+ RNAServiceValidationCleanFCN clean;
+ unsigned flow_data_index;
+};
+
+#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
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_base.cc author Ron Dempster <Ron.Dempster@sourcefire.com>
+
+#include "service_base.h"
+
+#include <limits.h>
+
+#include "service_api.h"
+#include "service_battle_field.h"
+#include "service_bgp.h"
+#include "service_bootp.h"
+#include "service_dcerpc.h"
+#include "service_direct_connect.h"
+#include "service_flap.h"
+#include "service_ftp.h"
+#include "service_irc.h"
+#include "service_lpr.h"
+#include "service_mdns.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_smtp.h"
+#include "service_snmp.h"
+#include "service_ssh.h"
+#include "service_ssl.h"
+#include "service_telnet.h"
+#include "service_tftp.h"
+#include "appid_flow_data.h"
+#include "fw_appid.h"
+#include "lua_detector_api.h"
+#include "lua_detector_module.h"
+#include "util/ip_funcs.h"
+#include "detector_plugins/detector_dns.h"
+#include "detector_plugins/detector_pattern.h"
+#include "detector_plugins/detector_sip.h"
+#include "log/messages.h"
+#include "main/snort_debug.h"
+#include "search_engines/search_tool.h"
+#include "utils/util.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
+
+/* If this is greater than 1, more than 1 service detector can be searched for
+ * and tried per flow based on port/pattern (if a valid detector doesn't
+ * already exist). */
+#define MAX_CANDIDATE_SERVICES 10
+
+static void* service_flowdata_get(AppIdData* flow, unsigned service_id);
+static int service_flowdata_add(AppIdData* flow, void* data, unsigned service_id, AppIdFreeFCN
+ fcn);
+static void AppIdAddHostInfo(AppIdData* flow, SERVICE_HOST_INFO_CODE code, const void* info);
+static int AppIdAddDHCP(AppIdData* flowp, unsigned op55_len, const uint8_t* op55, unsigned
+ op60_len, const uint8_t* op60, const uint8_t* mac);
+static void AppIdAddHostIP(AppIdData* flow, const uint8_t* mac, uint32_t ip4,
+ int32_t zone, uint32_t subnetmask, uint32_t leaseSecs, uint32_t router);
+static void AppIdAddSMBData(AppIdData* flow, unsigned major, unsigned minor, uint32_t flags);
+static void AppIdServiceAddMisc(AppIdData* flow, AppId miscId);
+
+const ServiceApi serviceapi =
+{
+ &service_flowdata_get,
+ &service_flowdata_add,
+ &AppIdEarlySessionCreate,
+ &AppIdFlowdataAddId,
+ &AppIdAddDHCP,
+ &AppIdAddHostIP,
+ &AppIdAddSMBData,
+ &AppIdServiceAddService,
+ &AppIdServiceFailService,
+ &AppIdServiceInProcess,
+ &AppIdServiceIncompatibleData,
+ &AppIdAddHostInfo,
+ &AppIdAddPayload,
+ &AppIdAddUser,
+ &AppIdServiceAddServiceSubtype,
+ &AppIdServiceAddMisc,
+ &AppIdAddDnsQueryInfo,
+ &AppIdAddDnsResponseInfo,
+ &AppIdResetDnsInfo,
+};
+
+#ifdef SERVICE_DEBUG
+static const char* serviceIdStateName[] =
+{
+ "NEW",
+ "VALID",
+ "PORT",
+ "PATTERN",
+ "BRUTE_FORCE"
+};
+#endif
+
+static RNAServiceElement* ftp_service = nullptr;
+
+static ServicePatternData* free_pattern_data;
+
+/*C service API */
+static void ServiceRegisterPattern(RNAServiceValidationFCN fcn,
+ IpProtocol proto, const uint8_t* pattern, unsigned size,
+ int position, struct Detector* userdata, int provides_user,
+ const char* name, ServiceConfig* pServiceConfig);
+static void CServiceRegisterPattern(RNAServiceValidationFCN fcn, IpProtocol proto,
+ const uint8_t* pattern, unsigned size,
+ int position, const char* name, AppIdConfig* pConfig);
+static void ServiceRegisterPatternUser(RNAServiceValidationFCN fcn, IpProtocol proto,
+ const uint8_t* pattern, unsigned size,
+ int position, const char* name, AppIdConfig* pConfig);
+static int CServiceAddPort(RNAServiceValidationPort* pp, RNAServiceValidationModule* svm,
+ AppIdConfig* pConfig);
+static void CServiceRemovePorts(RNAServiceValidationFCN validate, AppIdConfig* pConfig);
+
+static IniServiceAPI svc_init_api =
+{
+ &CServiceRegisterPattern,
+ &CServiceAddPort,
+ &CServiceRemovePorts,
+ &ServiceRegisterPatternUser,
+ &appSetServiceValidator,
+ 0,
+ 0,
+ nullptr
+};
+
+static CleanServiceAPI svc_clean_api =
+{
+ nullptr
+};
+
+extern RNAServiceValidationModule timbuktu_service_mod;
+extern RNAServiceValidationModule bit_service_mod;
+extern RNAServiceValidationModule tns_service_mod;
+extern RNAServiceValidationModule http_service_mod;
+
+static RNAServiceValidationModule* static_service_list[] =
+{
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ &bgp_service_mod,
+ &bootp_service_mod,
+ &dcerpc_service_mod,
+#endif
+ &dns_service_mod,
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ &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,
+#endif
+ &smtp_service_mod,
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ &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,
+#endif
+ &pattern_service_mod,
+ &http_service_mod
+};
+
+struct ServiceMatch
+{
+ struct ServiceMatch* next;
+ unsigned count;
+ unsigned size;
+ RNAServiceElement* svc;
+};
+
+static DHCPInfo* dhcp_info_free_list;
+static FpSMBData* smb_data_free_list;
+static unsigned smOrderedListSize = 0;
+static ServiceMatch** smOrderedList = nullptr;
+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) != nullptr)
+ {
+ free_service_match = match->next;
+ snort_free(match);
+ }
+}
+
+int AddFTPServiceState(AppIdData* 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*)snort_calloc(sizeof(ServiceMatch));
+}
+
+static int pattern_match(void* id, void*, int index, void* data, void*)
+{
+ ServiceMatch** matches = (ServiceMatch**)data;
+ ServicePatternData* pd = (ServicePatternData*)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
+ {
+ sm=allocServiceMatch();
+ sm->count++;
+ sm->svc = pd->svc;
+ sm->size = pd->size;
+ sm->next = *matches;
+ *matches = sm;
+ }
+ return 0;
+}
+
+AppId getPortServiceId(IpProtocol proto, uint16_t port, const AppIdConfig* pConfig)
+{
+ AppId appId;
+
+ if (proto == IpProtocol::TCP)
+ appId = pConfig->tcp_port_only[port];
+ else if (proto == IpProtocol::UDP)
+ appId = pConfig->udp_port_only[port];
+ else
+ appId = pConfig->ip_protocol[(uint16_t)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 RNAServiceElement* AppIdGetNexServiceByPort(
+ IpProtocol protocol,
+ uint16_t port,
+ const RNAServiceElement* const lasService,
+ AppIdData* rnaData,
+ const AppIdConfig* pConfig
+ )
+{
+ RNAServiceElement* service = nullptr;
+ SF_LIST* list = nullptr;
+
+ if (AppIdServiceDetectionLevel(rnaData) == 1)
+ {
+ unsigned remappedPort = sslPortRemap(port);
+ if (remappedPort)
+ list = pConfig->serviceConfig.tcp_services[remappedPort];
+ }
+ else if (protocol == IpProtocol::TCP)
+ {
+ list = pConfig->serviceConfig.tcp_services[port];
+ }
+ else
+ {
+ list = pConfig->serviceConfig.udp_services[port];
+ }
+
+ if (list)
+ {
+ SF_LNODE* iter = nullptr;
+
+ service = (RNAServiceElement*)sflist_first(list, &iter);
+ if (lasService)
+ {
+ while ( service && ((service->validate != lasService->validate) ||
+ (service->userdata != lasService->userdata)))
+ service = (RNAServiceElement*)sflist_next(&iter);
+ if (service)
+ service = (RNAServiceElement*)sflist_next(&iter);
+ }
+ }
+
+#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 RNAServiceElement* AppIdNexServiceByPattern(AppIdServiceIDState* id_state
+#ifdef SERVICE_DEBUG
+#if SERVICE_DEBUG_PORT
+ , uint16_t port
+#endif
+#endif
+ )
+{
+ RNAServiceElement* service = nullptr;
+
+ while (id_state->currenService)
+ {
+ id_state->currenService = id_state->currenService->next;
+ if (id_state->currenService && id_state->currenService->svc->current_ref_count)
+ {
+ service = id_state->currenService->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;
+}
+
+const RNAServiceElement* ServiceGetServiceElement(RNAServiceValidationFCN fcn, struct
+ Detector* userdata,
+ AppIdConfig* pConfig)
+{
+ RNAServiceElement* 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 nullptr;
+}
+
+static void ServiceRegisterPattern(RNAServiceValidationFCN fcn,
+ IpProtocol proto, const uint8_t* pattern, unsigned size,
+ int position, struct Detector* userdata, int provides_user,
+ const char* name, ServiceConfig* pServiceConfig)
+{
+ SearchTool** patterns;
+ ServicePatternData** pd_list;
+ int* count;
+ ServicePatternData* pd;
+ RNAServiceElement** list;
+ RNAServiceElement* li;
+
+ if ((IpProtocol)proto == IpProtocol::TCP)
+ {
+ patterns = &pServiceConfig->tcp_patterns;
+ pd_list = &pServiceConfig->tcp_pattern_data;
+
+ count = &pServiceConfig->tcp_pattern_count;
+ list = &pServiceConfig->tcp_service_list;
+ }
+ else if ((IpProtocol)proto == IpProtocol::UDP)
+ {
+ patterns = &pServiceConfig->udp_patterns;
+ pd_list = &pServiceConfig->udp_pattern_data;
+
+ count = &pServiceConfig->udp_pattern_count;
+ list = &pServiceConfig->udp_service_list;
+ }
+ else
+ {
+ ErrorMessage("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)
+ {
+ li = (RNAServiceElement*)snort_calloc(sizeof(RNAServiceElement));
+ 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 = new SearchTool("ac_full");
+ if (!(*patterns))
+ {
+ ErrorMessage("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
+ pd = (ServicePatternData*)snort_calloc(sizeof(ServicePatternData));
+
+ pd->svc = li;
+ pd->size = size;
+ pd->position = position;
+ (*patterns)->add(pattern, size, pd, false);
+ (*count)++;
+ pd->next = *pd_list;
+ *pd_list = pd;
+ li->ref_count++;
+}
+
+void ServiceRegisterPatternDetector(RNAServiceValidationFCN fcn,
+ IpProtocol proto, const uint8_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, IpProtocol proto,
+ const uint8_t* pattern, unsigned size, int position, const char* name, AppIdConfig* pConfig)
+{
+ ServiceRegisterPattern(fcn, proto, pattern, size, position, nullptr, 1, name,
+ &pConfig->serviceConfig);
+}
+
+static void CServiceRegisterPattern(RNAServiceValidationFCN fcn, IpProtocol proto,
+ const uint8_t* pattern, unsigned size,
+ int position, const char* name,
+ AppIdConfig* pConfig)
+{
+ ServiceRegisterPattern(fcn, proto, pattern, size, position, nullptr, 0, name,
+ &pConfig->serviceConfig);
+}
+
+static void RemoveServicePortsByType(RNAServiceValidationFCN validate, SF_LIST** services,
+ RNAServiceElement* list, struct Detector* userdata)
+{
+ RNAServiceElement* li;
+ unsigned i;
+
+ for (li=list; li; li=li->next)
+ {
+ if (li->validate == validate && li->userdata == userdata)
+ break;
+ }
+ if (li == nullptr)
+ return;
+
+ for (i=0; i < RNA_SERVICE_MAX_PORT; i++)
+ {
+ SF_LIST* listTmp;
+
+ if ( ( listTmp = services[i] ) )
+ {
+ SF_LNODE* iter;
+ RNAServiceElement* liTmp;
+
+ liTmp = (RNAServiceElement*)sflist_first(listTmp, &iter);
+ while (liTmp)
+ {
+ if (liTmp == li)
+ {
+ li->ref_count--;
+ sflist_remove_node(listTmp, iter);
+ // FIXIT - M Revisit this for better solution to calling sflist_first after
+ // deleting a node... ultimate solution for use of sflist would be move
+ // to STL
+ liTmp = (RNAServiceElement*)sflist_first(listTmp, &iter);
+ }
+ else
+ liTmp = (RNAServiceElement*)sflist_next(&iter);
+ }
+ }
+ }
+}
+
+/**
+ * \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(ServiceConfig* pServiceConfig)
+{
+ int i;
+
+ for (i=0; i<RNA_SERVICE_MAX_PORT; i++)
+ {
+ if (pServiceConfig->tcp_services[i])
+ {
+ sflist_free(pServiceConfig->tcp_services[i]);
+ pServiceConfig->tcp_services[i] = nullptr;
+ }
+ }
+ for (i=0; i<RNA_SERVICE_MAX_PORT; i++)
+ {
+ if (pServiceConfig->udp_services[i])
+ {
+ sflist_free(pServiceConfig->udp_services[i]);
+ pServiceConfig->udp_services[i] = nullptr;
+ }
+ }
+ for (i=0; i<RNA_SERVICE_MAX_PORT; i++)
+ {
+ if (pServiceConfig->udp_reversed_services[i])
+ {
+ sflist_free(pServiceConfig->udp_reversed_services[i]);
+ pServiceConfig->udp_reversed_services[i] = nullptr;
+ }
+ }
+}
+
+void ServiceRemovePorts(RNAServiceValidationFCN validate, struct Detector* userdata,
+ AppIdConfig* 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, AppIdConfig* pConfig)
+{
+ ServiceRemovePorts(validate, nullptr, pConfig);
+}
+
+int ServiceAddPort(RNAServiceValidationPort* pp, RNAServiceValidationModule* svm,
+ struct Detector* userdata, AppIdConfig* pConfig)
+{
+ SF_LIST** services;
+ RNAServiceElement** list = nullptr;
+ RNAServiceElement* li;
+ RNAServiceElement* serviceElement;
+ uint8_t isAllocated = 0;
+
+ DebugFormat(DEBUG_INSPECTOR, "Adding service %s for protocol %u on port %u, %p",
+ svm->name, (unsigned)pp->proto, (unsigned)pp->port, (void*)pp->validate);
+ if (pp->proto == IpProtocol::TCP)
+ {
+ services = pConfig->serviceConfig.tcp_services;
+ list = &pConfig->serviceConfig.tcp_service_list;
+ }
+ else if (pp->proto == IpProtocol::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
+ {
+ ErrorMessage("Service %s did not have a valid protocol (%u)\n",
+ 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)
+ {
+ li = new RNAServiceElement;
+ 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 == IpProtocol::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])
+ {
+ services[pp->port] = (SF_LIST*)snort_alloc(sizeof(SF_LIST));
+ sflist_init(services[pp->port]);
+ }
+
+ /*search and add if not present. */
+ SF_LNODE* iter = nullptr;
+ for (serviceElement = (RNAServiceElement*)sflist_first(services[pp->port], &iter);
+ serviceElement && (serviceElement != li);
+ serviceElement = (RNAServiceElement*)sflist_next(&iter))
+ ;
+
+ if (!serviceElement)
+ {
+ if (sflist_add_tail(services[pp->port], li))
+ {
+ ErrorMessage("Could not add %s, service for protocol %u on port %u", svm->name,
+ (unsigned)pp->proto, (unsigned)pp->port);
+ if (isAllocated)
+ {
+ *list = li->next;
+ snort_free(li);
+ }
+ return -1;
+ }
+ }
+
+ li->ref_count++;
+ return 0;
+}
+
+static int CServiceAddPort(RNAServiceValidationPort* pp, RNAServiceValidationModule* svm,
+ AppIdConfig* pConfig)
+{
+ return ServiceAddPort(pp, svm, nullptr, pConfig);
+}
+
+int serviceLoadForConfigCallback(void* symbol, AppIdConfig* pConfig)
+{
+ static unsigned service_module_index = 0;
+ RNAServiceValidationModule* svm = (RNAServiceValidationModule*)symbol;
+ RNAServiceValidationPort* pp;
+
+ if (service_module_index >= 65536)
+ {
+ ErrorMessage("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))
+ {
+ ErrorMessage("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**, uint32_t instance_id, AppIdConfig* pConfig)
+{
+ unsigned i;
+
+ svc_init_api.instance_id = instance_id;
+ svc_init_api.debug = pAppidActiveConfig->mod_config->debug;
+ svc_init_api.pAppidConfig = pConfig;
+
+ for (i=0; i<sizeof(static_service_list)/sizeof(*static_service_list); i++)
+ {
+ if (serviceLoadForConfigCallback(static_service_list[i], pConfig))
+ return -1;
+ }
+
+ return 0;
+}
+
+int ReloadServiceModules(AppIdConfig* pConfig)
+{
+ RNAServiceValidationModule* svm;
+ RNAServiceValidationPort* pp;
+
+ svc_init_api.debug = app_id_debug;
+ svc_init_api.pAppidConfig = pConfig;
+
+ // active_service_list contains both service modules and services associated with
+ // detector modules
+ for (svm=pConfig->serviceConfig.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(AppIdConfig*)
+{
+ luaModuleInitAllServices();
+}
+
+void ServiceFinalize(AppIdConfig* pConfig)
+{
+ if (pConfig->serviceConfig.tcp_patterns)
+ pConfig->serviceConfig.tcp_patterns->prep();
+ if (pConfig->serviceConfig.udp_patterns)
+ pConfig->serviceConfig.udp_patterns->prep();
+}
+
+void UnconfigureServices(AppIdConfig* pConfig)
+{
+ RNAServiceElement* li;
+ ServicePatternData* pd;
+ RNAServiceValidationModule* svm;
+
+ svc_clean_api.pAppidConfig = pConfig;
+
+ if (pConfig->serviceConfig.tcp_patterns)
+ {
+ delete pConfig->serviceConfig.tcp_patterns;
+ pConfig->serviceConfig.tcp_patterns = nullptr;
+ }
+ // 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) != nullptr)
+ li->ref_count--;
+ pConfig->serviceConfig.tcp_pattern_data = pd->next;
+ pd->next = free_pattern_data;
+ free_pattern_data = pd;
+ }
+ if (pConfig->serviceConfig.udp_patterns)
+ {
+ delete pConfig->serviceConfig.udp_patterns;
+ pConfig->serviceConfig.udp_patterns = nullptr;
+ }
+ while (pConfig->serviceConfig.udp_pattern_data)
+ {
+ pd = pConfig->serviceConfig.udp_pattern_data;
+ if ((li = pd->svc) != nullptr)
+ 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(AppIdConfig* pConfig)
+{
+ RNAServiceValidationModule* 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))
+ {
+ ErrorMessage("Error initializing service %s\n",svm->name);
+ }
+ else
+ {
+ DebugFormat(DEBUG_INSPECTOR,"Initialized service %s\n",svm->name);
+ }
+ }
+ }
+
+ ServiceInit(pConfig);
+}
+
+void CleanupServices(AppIdConfig* /*pConfig*/)
+{
+// FIXIT-H: Without this defined, pConfig is unused parameter
+#ifdef RNA_FULL_CLEANUP
+ ServicePatternData* pattern;
+ RNAServiceElement* se;
+ ServiceMatch* sm;
+ RNAServiceValidationModule* svm;
+ FpSMBData* sd;
+ DHCPInfo* info;
+
+ svc_clean_api.pAppidConfig = pConfig;
+
+ if (pConfig->serviceConfig.tcp_patterns)
+ {
+ delete pConfig->serviceConfig.tcp_patterns;
+ pConfig->serviceConfig.tcp_patterns = nullptr;
+ }
+ if (pConfig->serviceConfig.udp_patterns)
+ {
+ delete pConfig->serviceConfig.udp_patterns;
+ pConfig->serviceConfig.udp_patterns = nullptr;
+ }
+ while ((pattern=pConfig->serviceConfig.tcp_pattern_data))
+ {
+ pConfig->serviceConfig.tcp_pattern_data = pattern->next;
+ snort_free(pattern);
+ }
+ while ((pattern=pConfig->serviceConfig.udp_pattern_data))
+ {
+ pConfig->serviceConfig.udp_pattern_data = pattern->next;
+ snort_free(pattern);
+ }
+ while ((pattern=free_pattern_data))
+ {
+ free_pattern_data = pattern->next;
+ snort_free(pattern);
+ }
+ while ((se=pConfig->serviceConfig.tcp_service_list))
+ {
+ pConfig->serviceConfig.tcp_service_list = se->next;
+ snort_free(se);
+ }
+ while ((se=pConfig->serviceConfig.udp_service_list))
+ {
+ pConfig->serviceConfig.udp_service_list = se->next;
+ snort_free(se);
+ }
+ while ((se=pConfig->serviceConfig.udp_reversed_service_list))
+ {
+ pConfig->serviceConfig.udp_reversed_service_list = se->next;
+ snort_free(se);
+ }
+ while ((sd = smb_data_free_list))
+ {
+ smb_data_free_list = sd->next;
+ snort_free(sd);
+ }
+ while ((info = dhcp_info_free_list))
+ {
+ dhcp_info_free_list = info->next;
+ snort_free(info);
+ }
+ while ((sm = free_service_match))
+ {
+ free_service_match = sm->next;
+ snort_free(sm);
+ }
+ cleanupFreeServiceMatch();
+ if (smOrderedList)
+ {
+ // FIXIT - M - still allocated with calloc/realloc - vector coming soon...
+ 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 RNAServiceElement* AppIdGetServiceByPattern(const Packet* pkt, IpProtocol proto,
+ const int, AppIdServiceIDState* id_state, const ServiceConfig* pServiceConfig)
+{
+ SearchTool* patterns = nullptr;
+ ServiceMatch* match_list;
+ ServiceMatch* sm;
+ uint32_t count;
+ uint32_t i;
+ RNAServiceElement* service = nullptr;
+
+ if (proto == IpProtocol::TCP)
+ patterns = pServiceConfig->tcp_patterns;
+ else
+ patterns = pServiceConfig->udp_patterns;
+
+ if (!patterns)
+ {
+#ifdef SERVICE_DEBUG
+#if SERVICE_DEBUG_PORT
+ if (pkt->ptrs.dp == SERVICE_DEBUG_PORT || pkt->ptrs.sp == SERVICE_DEBUG_PORT)
+#endif
+ fprintf(SF_DEBUG_FILE, "Pattern bailing due to no patterns\n");
+#endif
+ return nullptr;
+ }
+
+ if (!smOrderedList)
+ {
+ smOrderedListSize = 32;
+ // FIXIT - H - using calloc because this may be realloc'ed later, change to vector asap
+ smOrderedList = (ServiceMatch**)calloc(smOrderedListSize, sizeof(ServiceMatch*));
+ if (!smOrderedList)
+ {
+ ErrorMessage("Pattern bailing due to failed allocation");
+ return nullptr;
+ }
+ }
+
+#ifdef SERVICE_DEBUG
+#if SERVICE_DEBUG_PORT
+ if (pkt->ptrs.dp == SERVICE_DEBUG_PORT || pkt->ptrs.sp == SERVICE_DEBUG_PORT)
+ {
+#endif
+ fprintf(SF_DEBUG_FILE, "Matching\n");
+ DumpHex(SF_DEBUG_FILE, pkt->data, pkt->dsize);
+#if SERVICE_DEBUG_PORT
+}
+
+#endif
+#endif
+ /*FRE didn't search */
+ match_list = nullptr;
+ patterns->find_all((char*)pkt->data, pkt->dsize, &pattern_match, false, (void*)&match_list);
+
+ count = 0;
+ for (sm=match_list; sm; sm=sm->next)
+ {
+ if (count >= smOrderedListSize)
+ {
+ ServiceMatch** tmp;
+ smOrderedListSize *= 2;
+ tmp = (ServiceMatch**)realloc(smOrderedList, smOrderedListSize *
+ sizeof(*smOrderedList));
+ if (!tmp)
+ {
+ ErrorMessage("Realloc failure %u\n",smOrderedListSize);
+ smOrderedListSize /= 2;
+
+ /*free the remaining elements. */
+ AppIdFreeServiceMatchList(sm);
+
+ break;
+ }
+ ErrorMessage("Realloc %u\n",smOrderedListSize);
+
+ smOrderedList = tmp;
+ }
+
+ smOrderedList[count++] = sm;
+ }
+
+ if (!count)
+ return nullptr;
+
+ 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 = nullptr;
+
+ service = smOrderedList[0]->svc;
+
+ if (id_state)
+ {
+ id_state->svc = service;
+ if (id_state->serviceList != nullptr)
+ {
+ AppIdFreeServiceMatchList(id_state->serviceList);
+ }
+ id_state->serviceList = smOrderedList[0];
+ id_state->currenService = smOrderedList[0];
+ }
+ else
+ AppIdFreeServiceMatchList(smOrderedList[0]);
+
+#ifdef SERVICE_DEBUG
+#if SERVICE_DEBUG_PORT
+ if (pkt->ptrs.dp == SERVICE_DEBUG_PORT || pkt->ptrs.sp == SERVICE_DEBUG_PORT)
+#endif
+ fprintf(SF_DEBUG_FILE, "Pattern service for protocol %u (%u->%u), %s\n",
+ (unsigned)proto, (unsigned)pkt->ptrs.sp, (unsigned)pkt->ptrs.dp,
+ (service && service->name) ? service->name.c_str() : "UNKNOWN");
+#endif
+ return service;
+}
+
+static inline RNAServiceElement* AppIdGetServiceByBruteForce(
+ IpProtocol protocol,
+ const RNAServiceElement* lasService,
+ const AppIdConfig* pConfig
+ )
+{
+ RNAServiceElement* service;
+
+ if (lasService)
+ service = lasService->next;
+ else
+ service = ((protocol == IpProtocol::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(AppIdData*, SERVICE_HOST_INFO_CODE, const void*)
+{
+}
+
+void AppIdFreeDhcpData(DhcpFPData* dd)
+{
+ snort_free(dd);
+}
+
+static int AppIdAddDHCP(AppIdData* 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 = (DhcpFPData*)snort_calloc(sizeof(*rdd));
+ 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(AppIdData* 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
+ info = (DHCPInfo*)snort_calloc(sizeof(DHCPInfo));
+
+ 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(AppIdData* flow, unsigned major, unsigned minor, uint32_t flags)
+{
+ FpSMBData* sd;
+
+ if (flags & FINGERPRINT_UDP_FLAGS_XENIX)
+ return;
+
+ if (smb_data_free_list)
+ {
+ sd = smb_data_free_list;
+ smb_data_free_list = sd->next;
+ }
+ else
+ sd = (FpSMBData*)snort_calloc(sizeof(FpSMBData));
+
+ 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(AppIdData* flow, const Packet* pkt, int dir,
+ const RNAServiceElement* svc_element,
+ AppId appId, const char* vendor, const char* version)
+{
+ AppIdServiceIDState* id_state;
+ uint16_t port;
+ const sfip_t* ip;
+
+ if (!flow || !pkt || !svc_element)
+ {
+ ErrorMessage("Invalid arguments to absinthe_add_appId");
+ return SERVICE_EINVALID;
+ }
+
+ flow->serviceData = svc_element;
+
+ if (vendor)
+ {
+ if (flow->serviceVendor)
+ snort_free(flow->serviceVendor);
+ flow->serviceVendor = snort_strdup(vendor);
+ }
+ if (version)
+ {
+ if (flow->serviceVersion)
+ snort_free(flow->serviceVersion);
+ flow->serviceVersion = snort_strdup(version);
+ }
+ setAppIdFlag(flow, APPID_SESSION_SERVICE_DETECTED);
+ flow->serviceAppId = appId;
+
+ checkSandboxDetection(appId);
+
+ if (getAppIdFlag(flow, APPID_SESSION_IGNORE_HOST))
+ return SERVICE_SUCCESS;
+
+ if (!getAppIdFlag(flow, APPID_SESSION_UDP_REVERSED))
+ {
+ if (dir == APP_ID_FROM_INITIATOR)
+ {
+ ip = pkt->ptrs.ip_api.get_dst();
+ port = pkt->ptrs.dp;
+ }
+ else
+ {
+ ip = pkt->ptrs.ip_api.get_src();
+ port = pkt->ptrs.sp;
+ }
+ if (flow->service_port)
+ port = flow->service_port;
+ }
+ else
+ {
+ if (dir == APP_ID_FROM_INITIATOR)
+ {
+ ip = pkt->ptrs.ip_api.get_src();
+ port = pkt->ptrs.sp;
+ }
+ else
+ {
+ ip = pkt->ptrs.ip_api.get_dst();
+ port = pkt->ptrs.dp;
+ }
+ }
+
+ /* If we ended up with UDP reversed, make sure we're pointing to the
+ * correct host tracker entry. */
+ if (getAppIdFlag(flow, APPID_SESSION_UDP_REVERSED))
+ {
+ flow->id_state = AppIdGetServiceIDState(ip, flow->proto, port,
+ AppIdServiceDetectionLevel(flow));
+ }
+
+ if (!(id_state = flow->id_state))
+ {
+ if (!(id_state = AppIdAddServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(
+ flow))))
+ {
+ ErrorMessage("Add service failed to create state");
+ return SERVICE_ENOMEM;
+ }
+ flow->id_state = id_state;
+ flow->service_ip = *ip;
+ flow->service_port = port;
+ }
+ else
+ {
+ if (id_state->serviceList)
+ {
+ AppIdFreeServiceMatchList(id_state->serviceList);
+ id_state->serviceList = nullptr;
+ id_state->currenService = nullptr;
+ }
+ if (!sfip_is_set(&flow->service_ip))
+ {
+ flow->service_ip = *ip;
+ flow->service_port = port;
+ }
+#ifdef SERVICE_DEBUG
+#if SERVICE_DEBUG_PORT
+ if (pkt->ptrs.dp == SERVICE_DEBUG_PORT || pkt->ptrs.sp == SERVICE_DEBUG_PORT)
+#endif
+ 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->ptrs.sp, (unsigned)pkt->ptrs.dp);
+#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;
+ id_state->last_detract.clear();
+ id_state->invalid_client_count = 0;
+ id_state->last_invalid_client.clear();
+ }
+ id_state->svc = svc_element;
+
+#ifdef SERVICE_DEBUG
+#if SERVICE_DEBUG_PORT
+ if (pkt->ptrs.dp == SERVICE_DEBUG_PORT || pkt->ptrs.sp == SERVICE_DEBUG_PORT)
+#endif
+ {
+ char ipstr[INET6_ADDRSTRLEN];
+
+ ipstr[0] = 0;
+ inet_ntop(sfip_family(&flow->service_ip), (void*)sfaddr_get_ptr(&flow->service_ip),
+ ipstr, sizeof(ipstr));
+ 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
+
+ if (!id_state->valid_count)
+ {
+ id_state->valid_count++;
+ id_state->invalid_client_count = 0;
+ id_state->last_invalid_client.clear();
+ id_state->detract_count = 0;
+ id_state->last_detract.clear();
+ }
+ else if (id_state->valid_count < STATE_ID_MAX_VALID_COUNT)
+ id_state->valid_count++;
+
+ /* Done looking for this session. */
+ id_state->searching = false;
+
+#ifdef SERVICE_DEBUG
+#if SERVICE_DEBUG_PORT
+ if (pkt->ptrs.dp == SERVICE_DEBUG_PORT || pkt->ptrs.sp == SERVICE_DEBUG_PORT)
+#endif
+ 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->ptrs.sp,
+ (unsigned)pkt->ptrs.dp);
+#endif
+ return SERVICE_SUCCESS;
+}
+
+int AppIdServiceAddServiceSubtype(AppIdData* flow, const Packet* pkt, int dir,
+ const RNAServiceElement* svc_element,
+ AppId appId, const char* vendor, const char* version,
+ RNAServiceSubtype* subtype)
+{
+ flow->subtype = subtype;
+ if (!svc_element->current_ref_count)
+ {
+#ifdef SERVICE_DEBUG
+#if SERVICE_DEBUG_PORT
+ if (pkt->ptrs.dp == SERVICE_DEBUG_PORT || pkt->ptrs.sp == SERVICE_DEBUG_PORT)
+#endif
+ 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->ptrs.sp, (unsigned)pkt->ptrs.dp);
+#endif
+ return SERVICE_SUCCESS;
+ }
+ return AppIdServiceAddServiceEx(flow, pkt, dir, svc_element, appId, vendor, version);
+}
+
+int AppIdServiceAddService(AppIdData* flow, const Packet* pkt, int dir,
+ const RNAServiceElement* svc_element,
+ AppId appId, const char* vendor, const char* version,
+ const RNAServiceSubtype* subtype)
+{
+ RNAServiceSubtype* new_subtype = nullptr;
+ RNAServiceSubtype* tmp_subtype;
+
+ if (!svc_element->current_ref_count)
+ {
+#ifdef SERVICE_DEBUG
+#if SERVICE_DEBUG_PORT
+ if (pkt->ptrs.dp == SERVICE_DEBUG_PORT || pkt->ptrs.sp == SERVICE_DEBUG_PORT)
+#endif
+ 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->ptrs.sp, (unsigned)pkt->ptrs.dp);
+#endif
+ return SERVICE_SUCCESS;
+ }
+
+ for (; subtype; subtype = subtype->next)
+ {
+ tmp_subtype = (RNAServiceSubtype*)snort_calloc(sizeof(RNAServiceSubtype));
+ if (subtype->service)
+ tmp_subtype->service = snort_strdup(subtype->service);
+
+ if (subtype->vendor)
+ tmp_subtype->vendor = snort_strdup(subtype->vendor);
+
+ if (subtype->version)
+ tmp_subtype->version = snort_strdup(subtype->version);
+
+ tmp_subtype->next = new_subtype;
+ new_subtype = tmp_subtype;
+ }
+ flow->subtype = new_subtype;
+ return AppIdServiceAddServiceEx(flow, pkt, dir, svc_element, appId, vendor, version);
+}
+
+int AppIdServiceInProcess(AppIdData* flow, const Packet* pkt, int dir,
+ const RNAServiceElement* svc_element)
+{
+ AppIdServiceIDState* id_state;
+
+ if (!flow || !pkt)
+ {
+ ErrorMessage("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 (!(id_state = flow->id_state))
+ {
+ uint16_t port;
+ const sfip_t* ip;
+
+ ip = pkt->ptrs.ip_api.get_src();
+ port = flow->service_port ? flow->service_port : pkt->ptrs.sp;
+
+ if (!(id_state = AppIdAddServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(
+ flow))))
+ {
+ ErrorMessage("In-process service failed to create state");
+ return SERVICE_ENOMEM;
+ }
+ flow->id_state = id_state;
+ flow->service_ip = *ip;
+ flow->service_port = port;
+ id_state->state = SERVICE_ID_NEW;
+ id_state->svc = svc_element;
+ }
+ else
+ {
+ if (!sfip_is_set(&flow->service_ip))
+ {
+ const sfip_t* ip = pkt->ptrs.ip_api.get_src();
+ flow->service_ip = *ip;
+ if (!flow->service_port)
+ flow->service_port = pkt->ptrs.sp;
+ }
+#ifdef SERVICE_DEBUG
+#if SERVICE_DEBUG_PORT
+ if (pkt->ptrs.dp == SERVICE_DEBUG_PORT || pkt->ptrs.sp == SERVICE_DEBUG_PORT)
+#endif
+ 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->ptrs.sp,
+ (unsigned)pkt->ptrs.dp,
+ svc_element->validate, svc_element->name ? : "UNKNOWN");
+#endif
+ }
+
+#ifdef SERVICE_DEBUG
+#if SERVICE_DEBUG_PORT
+ if (pkt->ptrs.dp == SERVICE_DEBUG_PORT || pkt->ptrs.sp == SERVICE_DEBUG_PORT)
+#endif
+ {
+ char ipstr[INET6_ADDRSTRLEN];
+
+ ipstr[0] = 0;
+ inet_ntop(sfip_family(&flow->service_ip), (void*)sfaddr_get_ptr(&flow->service_ip),
+ ipstr, sizeof(ipstr));
+ fprintf(SF_DEBUG_FILE, "Inprocess: %s:%u:%u %p %d\n", ipstr, (unsigned)flow->proto,
+ (unsigned)flow->service_port,
+ id_state, (int)id_state->state);
+ }
+#endif
+
+#ifdef SERVICE_DEBUG
+#if SERVICE_DEBUG_PORT
+ if (pkt->ptrs.dp == SERVICE_DEBUG_PORT || pkt->ptrs.sp == SERVICE_DEBUG_PORT)
+#endif
+ fprintf(SF_DEBUG_FILE, "Service for protocol %u on port %u is in process (%u->%u), %s\n",
+ (unsigned)flow->proto, (unsigned)flow->service_port, (unsigned)pkt->ptrs.sp,
+ (unsigned)pkt->ptrs.dp,
+ svc_element->name ? : "UNKNOWN");
+#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(AppIdData* flow, const Packet* pkt, int dir,
+ const RNAServiceElement* svc_element, unsigned flow_data_index, const AppIdConfig*)
+{
+ AppIdServiceIDState* id_state;
+
+ if (!flow || !pkt)
+ {
+ ErrorMessage("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 == nullptr) /* we're
+ working
+ on a list
+ of
+ detectors,
+ and... */
+ && (flow->candidate_service_list != nullptr)
+ && (flow->id_state != nullptr) )
+ {
+ if (sflist_count(flow->candidate_service_list) != 0) /* it's
+ not empty
+ */
+ {
+ return SERVICE_SUCCESS;
+ }
+ else if ( (flow->num_candidate_services_tried >= MAX_CANDIDATE_SERVICES) /* or
+ it's
+ empty,
+ but we're
+ tried
+ everything
+ we're
+ going to
+ try */
+ || (flow->id_state->state == SERVICE_ID_BRUTE_FORCE) )
+ {
+ 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;
+ }
+
+ if (!(id_state = flow->id_state))
+ {
+ uint16_t port;
+ const sfip_t* ip;
+
+ ip = pkt->ptrs.ip_api.get_src();
+ port = flow->service_port ? flow->service_port : pkt->ptrs.sp;
+
+ if (!(id_state = AppIdAddServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(
+ flow))))
+ {
+ ErrorMessage("Incompatible service failed to create state");
+ return SERVICE_ENOMEM;
+ }
+ flow->id_state = id_state;
+ flow->service_ip = *ip;
+ flow->service_port = port;
+ id_state->state = SERVICE_ID_NEW;
+ id_state->svc = svc_element;
+ }
+ else
+ {
+ if (!sfip_is_set(&flow->service_ip))
+ {
+ const sfip_t* ip = pkt->ptrs.ip_api.get_src();
+ flow->service_ip = *ip;
+ if (!flow->service_port)
+ flow->service_port = pkt->ptrs.sp;
+#ifdef SERVICE_DEBUG
+#if SERVICE_DEBUG_PORT
+ if (pkt->ptrs.dp == SERVICE_DEBUG_PORT || pkt->ptrs.sp == SERVICE_DEBUG_PORT)
+#endif
+ fprintf(SF_DEBUG_FILE,
+ "service_IC: Changed State to %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->ptrs.sp, (unsigned)pkt->ptrs.dp, id_state->invalid_client_count,
+ (id_state->svc && id_state->svc->name) ? id_state->svc->name : "UNKNOWN");
+#endif
+ }
+ id_state->reset_time = 0;
+ }
+
+#ifdef SERVICE_DEBUG
+#if SERVICE_DEBUG_PORT
+ if (pkt->ptrs.dp == SERVICE_DEBUG_PORT || pkt->ptrs.sp == SERVICE_DEBUG_PORT)
+#endif
+ 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->ptrs.sp, (unsigned)pkt->ptrs.dp, id_state->invalid_client_count,
+ (id_state->svc && id_state->svc->name) ? id_state->svc->name : "UNKNOWN");
+#endif
+
+#ifdef SERVICE_DEBUG
+#if SERVICE_DEBUG_PORT
+ if (pkt->ptrs.dp == SERVICE_DEBUG_PORT || pkt->ptrs.sp == SERVICE_DEBUG_PORT)
+#endif
+ {
+ char ipstr[INET6_ADDRSTRLEN];
+
+ ipstr[0] = 0;
+ inet_ntop(sfip_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
+
+#ifdef SERVICE_DEBUG
+#if SERVICE_DEBUG_PORT
+ if (pkt->ptrs.dp == SERVICE_DEBUG_PORT || pkt->ptrs.sp == SERVICE_DEBUG_PORT)
+#endif
+ {
+ char ipstr[INET6_ADDRSTRLEN];
+
+ ipstr[0] = 0;
+ inet_ntop(sfip_family(&flow->service_ip), (void*)sfaddr_get_ptr(&flow->service_ip),
+ ipstr, sizeof(ipstr));
+ fprintf(SF_DEBUG_FILE, "Incompat End: %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(AppIdData* flow, const Packet* pkt, int dir,
+ const RNAServiceElement* svc_element, unsigned flow_data_index, const AppIdConfig*)
+{
+ 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 == nullptr) /* we're
+ working
+ on a list
+ of
+ detectors,
+ and... */
+ && (flow->candidate_service_list != nullptr)
+ && (flow->id_state != nullptr) )
+ {
+ if (sflist_count(flow->candidate_service_list) != 0) /* it's
+ not empty
+ */
+ {
+ return SERVICE_SUCCESS;
+ }
+ else if ( (flow->num_candidate_services_tried >= MAX_CANDIDATE_SERVICES) /* or
+ it's
+ empty,
+ but we're
+ tried
+ everything
+ we're
+ going to
+ try */
+ || (flow->id_state->state == SERVICE_ID_BRUTE_FORCE) )
+ {
+ 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;
+ }
+
+ if (!(id_state = flow->id_state))
+ {
+ uint16_t port;
+ const sfip_t* ip;
+
+ ip = pkt->ptrs.ip_api.get_src();
+ port = flow->service_port ? flow->service_port : pkt->ptrs.sp;
+
+ if (!(id_state = AppIdAddServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(
+ flow))))
+ {
+ ErrorMessage("Fail service failed to create state");
+ return SERVICE_ENOMEM;
+ }
+ flow->id_state = id_state;
+ flow->service_ip = *ip;
+ flow->service_port = port;
+ id_state->state = SERVICE_ID_NEW;
+ id_state->svc = svc_element;
+ }
+ else
+ {
+ if (!sfip_is_set(&flow->service_ip))
+ {
+ const sfip_t* ip = pkt->ptrs.ip_api.get_src();
+ flow->service_ip = *ip;
+ if (!flow->service_port)
+ flow->service_port = pkt->ptrs.sp;
+ }
+#ifdef SERVICE_DEBUG
+#if SERVICE_DEBUG_PORT
+ if (pkt->ptrs.dp == SERVICE_DEBUG_PORT || pkt->ptrs.sp == SERVICE_DEBUG_PORT)
+#endif
+ 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->ptrs.sp, (unsigned)pkt->ptrs.dp, id_state->invalid_client_count,
+ id_state->valid_count,
+ (svc_element && svc_element->name) ? svc_element->name : "UNKNOWN");
+#endif
+ }
+ id_state->reset_time = 0;
+
+#ifdef SERVICE_DEBUG
+#if SERVICE_DEBUG_PORT
+ if (pkt->ptrs.dp == SERVICE_DEBUG_PORT || pkt->ptrs.sp == SERVICE_DEBUG_PORT)
+#endif
+ 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->ptrs.sp, (unsigned)pkt->ptrs.dp, id_state->invalid_client_count,
+ id_state->valid_count,
+ (svc_element && svc_element->name) ? svc_element->name : "UNKNOWN");
+#endif
+
+#ifdef SERVICE_DEBUG
+#if SERVICE_DEBUG_PORT
+ if (pkt->ptrs.dp == SERVICE_DEBUG_PORT || pkt->ptrs.sp == SERVICE_DEBUG_PORT)
+#endif
+ {
+ char ipstr[INET6_ADDRSTRLEN];
+
+ ipstr[0] = 0;
+ inet_ntop(sfip_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
+
+#ifdef SERVICE_DEBUG
+#if SERVICE_DEBUG_PORT
+ if (pkt->ptrs.dp == SERVICE_DEBUG_PORT || pkt->ptrs.sp == SERVICE_DEBUG_PORT)
+#endif
+ {
+ char ipstr[INET6_ADDRSTRLEN];
+
+ ipstr[0] = 0;
+ inet_ntop(sfip_family(&flow->service_ip), (void*)sfaddr_get_ptr(&flow->service_ip),
+ ipstr, sizeof(ipstr));
+ fprintf(SF_DEBUG_FILE, "Fail End: %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(AppIdData* flowp,
+ AppIdServiceIDState* id_state,
+ const sfip_t* client_ip,
+ unsigned timeout)
+{
+ /* 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;
+ id_state->last_invalid_client.clear();
+ id_state->valid_count = 0;
+ id_state->detract_count = 0;
+ id_state->last_detract.clear();
+ }
+ else
+ {
+ id_state->valid_count--;
+ id_state->last_invalid_client = *client_ip;
+ id_state->invalid_client_count = 0;
+ }
+ }
+ /* 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 (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;
+ id_state->last_invalid_client.clear();
+ id_state->valid_count = 0;
+ id_state->detract_count = 0;
+ id_state->last_detract.clear();
+ }
+ else
+ id_state->valid_count--;
+ }
+ }
+ }
+ /* If we were port/pattern searching and timed out, just restart over next
+ * time. */
+ else if (timeout && (flowp->candidate_service_list != nullptr))
+ {
+ id_state->state = SERVICE_ID_NEW;
+ }
+ /* If we were working on a port/pattern list of detectors, see if we
+ * should restart search (because of invalid clients) or just let it
+ * naturally continue onto brute force next. */
+ else if ( (flowp->candidate_service_list != nullptr)
+ && (id_state->state == SERVICE_ID_BRUTE_FORCE) )
+ {
+ /* If we're getting some invalid clients, keep retrying
+ * port/pattern search until we either find something or until we
+ * just see too many invalid clients. */
+ if ( (id_state->invalid_client_count > 0)
+ && (id_state->invalid_client_count < STATE_ID_INVALID_CLIENT_THRESHOLD) )
+ {
+ id_state->state = SERVICE_ID_NEW;
+ }
+ }
+
+ /* Done looking for this session. */
+ id_state->searching = false;
+}
+
+/**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 Packet may be nullptr when this function is called upon session timeout.
+ */
+void FailInProcessService(AppIdData* flowp, const AppIdConfig*)
+{
+ AppIdServiceIDState* id_state;
+
+ if (getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED|APPID_SESSION_UDP_REVERSED))
+ return;
+
+ id_state = AppIdGetServiceIDState(&flowp->service_ip, flowp->proto, flowp->service_port,
+ AppIdServiceDetectionLevel(flowp));
+
+#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;
+
+ // FIXIT - we need a Flow to get the ip address of client/server...
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ sfip_t* 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);
+
+ HandleFailure(flowp, id_state, tmp_ip, 1);
+#endif
+
+#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 nullptr). 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 RNAServiceElement* AppIdGetNexService(const Packet* p, const int dir,
+ AppIdData* rnaData, const AppIdConfig* pConfig, AppIdServiceIDState* id_state)
+{
+ auto proto = rnaData->proto;
+
+ /* If NEW, just advance onto trying ports. */
+ if (id_state->state == SERVICE_ID_NEW)
+ {
+ id_state->state = SERVICE_ID_PORT;
+ id_state->svc = nullptr;
+ }
+
+ /* See if there are any port detectors to try. If not, move onto patterns. */
+ if (id_state->state == SERVICE_ID_PORT)
+ {
+ id_state->svc = AppIdGetNexServiceByPort(proto, (uint16_t)((dir ==
+ APP_ID_FROM_RESPONDER) ? p->ptrs.sp : p->ptrs.dp), id_state->svc, rnaData, pConfig);
+ if (id_state->svc != nullptr)
+ {
+ return id_state->svc;
+ }
+ else
+ {
+ id_state->state = SERVICE_ID_PATTERN;
+ id_state->svc = nullptr;
+ if (id_state->serviceList != nullptr)
+ {
+ id_state->currenService = id_state->serviceList;
+ }
+ else
+ {
+ id_state->serviceList = nullptr;
+ id_state->currenService = nullptr;
+ }
+ }
+ }
+
+ if (id_state->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 ==
+ IpProtocol::UDP)
+ && !rnaData->tried_reverse_service )
+ {
+ SF_LNODE* iter;
+ AppIdServiceIDState* reverse_id_state;
+ const RNAServiceElement* reverse_service = nullptr;
+ const sfip_t* reverse_ip = p->ptrs.ip_api.get_src();
+ rnaData->tried_reverse_service = true;
+ if ((reverse_id_state = AppIdGetServiceIDState(reverse_ip, proto, p->ptrs.sp,
+ AppIdServiceDetectionLevel(rnaData))))
+ {
+ reverse_service = reverse_id_state->svc;
+ }
+ if ( reverse_service
+ || (pConfig->serviceConfig.udp_reversed_services[p->ptrs.sp] &&
+ (reverse_service = ( RNAServiceElement*)sflist_first(
+ pConfig->serviceConfig.udp_reversed_services[p->ptrs.sp], &iter)))
+ || (p->dsize && (reverse_service = AppIdGetServiceByPattern(p, proto,
+ dir, nullptr, &pConfig->serviceConfig))) )
+ {
+ id_state->svc = reverse_service;
+ return id_state->svc;
+ }
+ }
+ return nullptr;
+ }
+ /* Try pattern match detectors. If not, give up, and go to brute
+ * force. */
+ else /* APP_ID_FROM_RESPONDER */
+ {
+ if (id_state->serviceList == nullptr) /* no list yet (need to make one) */
+ {
+ id_state->svc = AppIdGetServiceByPattern(p, proto, dir, id_state,
+ &pConfig->serviceConfig);
+ }
+ else /* already have a pattern service list (just use it) */
+ {
+ id_state->svc = AppIdNexServiceByPattern(id_state
+#ifdef SERVICE_DEBUG
+#if SERVICE_DEBUG_PORT
+ , flow->service_port
+#endif
+#endif
+ );
+ }
+
+ if (id_state->svc != nullptr)
+ {
+ return id_state->svc;
+ }
+ else
+ {
+ id_state->state = SERVICE_ID_BRUTE_FORCE;
+ id_state->svc = nullptr;
+ return nullptr;
+ }
+ }
+ }
+
+ /* Don't do anything if it was in VALID or BRUTE FORCE. */
+ return nullptr;
+}
+
+int AppIdDiscoverService(Packet* p, const int dir, AppIdData* rnaData, const
+ AppIdConfig* pConfig)
+{
+ const sfip_t* ip;
+ int ret = SERVICE_NOMATCH;
+ const RNAServiceElement* service;
+ AppIdServiceIDState* id_state;
+ uint16_t port;
+ ServiceValidationArgs args;
+
+ /* Get packet info. */
+ auto proto = rnaData->proto;
+ if (sfip_is_set(&rnaData->service_ip))
+ {
+ ip = &rnaData->service_ip;
+ port = rnaData->service_port;
+ }
+ else
+ {
+ if (dir == APP_ID_FROM_RESPONDER)
+ {
+ ip = p->ptrs.ip_api.get_src();
+ port = p->ptrs.sp;
+ }
+ else
+ {
+ ip = p->ptrs.ip_api.get_src();
+ port = p->ptrs.dp;
+ }
+ }
+
+ /* Get host tracker state. */
+ id_state = rnaData->id_state;
+ if (id_state == nullptr)
+ {
+ id_state = AppIdGetServiceIDState(ip, proto, port, AppIdServiceDetectionLevel(rnaData));
+
+ /* Create it if it doesn't exist yet. */
+ if (id_state == nullptr)
+ {
+ if (!(id_state = AppIdAddServiceIDState(ip, proto, port,
+ AppIdServiceDetectionLevel(rnaData))))
+ {
+ ErrorMessage("Discover service failed to create state");
+ return SERVICE_ENOMEM;
+ }
+ memset(id_state, 0, sizeof(*id_state));
+ }
+ rnaData->id_state = id_state;
+ }
+
+ if (rnaData->serviceData == nullptr)
+ {
+ /* If a valid service already exists in host tracker, give it a try. */
+ if ((id_state->svc != nullptr) && (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)
+ && (rnaData->num_candidate_services_tried == 0)
+ && !id_state->searching )
+ {
+ rnaData->serviceData = AppIdGetServiceByBruteForce(proto, id_state->svc, pConfig);
+ id_state->svc = rnaData->serviceData;
+ }
+ }
+
+ args.data = p->data;
+ args.size = p->dsize;
+ 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 != nullptr)
+ {
+ service = rnaData->serviceData;
+ args.userdata = service->userdata;
+ ret = service->validate(&args);
+ if (ret == SERVICE_NOT_COMPATIBLE)
+ rnaData->got_incompatible_services = 1;
+ if (app_id_debug_session_flag)
+ LogMessage("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 (rnaData->candidate_service_list == nullptr)
+ {
+ rnaData->candidate_service_list = (SF_LIST*)snort_calloc(sizeof(SF_LIST));
+ sflist_init(rnaData->candidate_service_list);
+ rnaData->num_candidate_services_tried = 0;
+
+ /* This is our first time in for this session, and we're about to
+ * search for a service, because we don't have any solid history on
+ * this IP/port yet. If some other session is also currently
+ * searching on this host tracker entry, reset state here, so that
+ * we can start search over again with this session. */
+ if (id_state->searching)
+ id_state->state = SERVICE_ID_NEW;
+ id_state->searching = true;
+ }
+
+ /* See if we've got more detector(s) to add to the candidate list. */
+ if ( (id_state->state == SERVICE_ID_NEW)
+ || (id_state->state == SERVICE_ID_PORT)
+ || ((id_state->state == SERVICE_ID_PATTERN) && (dir == APP_ID_FROM_RESPONDER)) )
+ {
+ while (rnaData->num_candidate_services_tried < MAX_CANDIDATE_SERVICES)
+ {
+ const RNAServiceElement* tmp = AppIdGetNexService(p, dir, rnaData, pConfig,
+ id_state);
+ if (tmp != nullptr)
+ {
+ SF_LNODE* iter = nullptr;
+ /* Add to list (if not already there). */
+ service = (RNAServiceElement*)sflist_first(rnaData->candidate_service_list,
+ &iter);
+ while (service && (service != tmp))
+ service = (RNAServiceElement*)sflist_next(&iter);
+ if (service == nullptr)
+ {
+ sflist_add_tail(rnaData->candidate_service_list, (void*)tmp);
+ rnaData->num_candidate_services_tried++;
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ /* Run all of the detectors that we currently have. */
+ ret = SERVICE_INPROCESS;
+ SF_LNODE* iter;
+ service = (RNAServiceElement*)sflist_first(rnaData->candidate_service_list, &iter);
+ while (service)
+ {
+ int result;
+
+ args.userdata = service->userdata;
+ result = service->validate(&args);
+ if (result == SERVICE_NOT_COMPATIBLE)
+ rnaData->got_incompatible_services = 1;
+ if (app_id_debug_session_flag)
+ LogMessage("AppIdDbg %s %s returned %d\n", app_id_debug_session,
+ service->name ? service->name : "UNKNOWN", result);
+
+ if (result == SERVICE_SUCCESS)
+ {
+ ret = SERVICE_SUCCESS;
+ rnaData->serviceData = service;
+ sflist_free(rnaData->candidate_service_list);
+ rnaData->candidate_service_list = nullptr;
+ break; /* done */
+ }
+ else if (result != SERVICE_INPROCESS) /* fail */
+ {
+ sflist_remove_node(rnaData->candidate_service_list, iter);
+ service = (RNAServiceElement*)sflist_first(rnaData->candidate_service_list, &iter);
+ }
+ else
+ service = (RNAServiceElement*)sflist_next(&iter);
+ }
+
+ /* If we tried everything and found nothing, then fail. */
+ if (ret != SERVICE_SUCCESS)
+ {
+ if ( (sflist_count(rnaData->candidate_service_list) == 0)
+ && ( (rnaData->num_candidate_services_tried >= MAX_CANDIDATE_SERVICES)
+ || (id_state->state == SERVICE_ID_BRUTE_FORCE) ) )
+ {
+ AppIdServiceFailService(rnaData, p, dir, nullptr, APPID_SESSION_DATA_NONE,
+ pConfig);
+ ret = SERVICE_NOMATCH;
+ }
+ }
+ }
+
+ if (service != nullptr)
+ {
+ id_state->reset_time = 0;
+ }
+ else if (dir == APP_ID_FROM_RESPONDER) /* we have seen bidirectional exchange and have not
+ identified any service */
+ {
+ if (app_id_debug_session_flag)
+ LogMessage("AppIdDbg %s no RNA service detector\n", app_id_debug_session);
+ AppIdServiceFailService(rnaData, p, dir, nullptr, APPID_SESSION_DATA_NONE, pConfig);
+ ret = SERVICE_NOMATCH;
+ }
+
+ /* Handle failure exception cases in states. */
+ if ((ret != SERVICE_INPROCESS) && (ret != SERVICE_SUCCESS))
+ {
+ const sfip_t* tmp_ip;
+ if (dir == APP_ID_FROM_RESPONDER)
+ tmp_ip = p->ptrs.ip_api.get_dst();
+ else
+ tmp_ip = p->ptrs.ip_api.get_src();
+
+ if (rnaData->got_incompatible_services)
+ {
+ if (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, 0);
+ }
+
+ /* Can free up any pattern match lists if done with them. */
+ if ( (id_state->state == SERVICE_ID_BRUTE_FORCE)
+ || (id_state->state == SERVICE_ID_VALID) )
+ {
+ if (id_state->serviceList != nullptr)
+ {
+ AppIdFreeServiceMatchList(id_state->serviceList);
+ }
+ id_state->serviceList = nullptr;
+ id_state->currenService = nullptr;
+ }
+
+ return ret;
+}
+
+static void* service_flowdata_get(AppIdData* flow, unsigned service_id)
+{
+ return AppIdFlowdataGet(flow, service_id);
+}
+
+static int service_flowdata_add(AppIdData* 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 AppIdConfig* 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(AppIdData* flow, AppId miscId)
+{
+ if (flow != nullptr)
+ flow->miscAppId = miscId;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_base.h author Sourcefire Inc.
+
+#ifndef SERVICE_BASE_H
+#define SERVICE_BASE_H
+
+#include <cstdint>
+
+// FIXIT - there are conflicts with sfaddr_temp.h - sf_ip.h must be first address those asap
+#include "appid_api.h"
+#include "appid_flow_data.h"
+#include "service_api.h"
+#include "sfip/sf_ip.h"
+
+class AppIdConfig;
+class AppIdData;
+struct RNAServiceElement;
+struct DhcpFPData;
+struct FpSMBData;
+struct Packet;
+struct ServiceMatch;
+struct Detector;
+struct RNAServiceValidationPort;
+struct RNAServiceValidationModule;
+
+void CleanupServices(AppIdConfig*);
+void ReconfigureServices(AppIdConfig*);
+void UnconfigureServices(AppIdConfig*);
+void ServiceInit(AppIdConfig*);
+void ServiceFinalize(AppIdConfig*);
+void FailInProcessService(AppIdData*, const AppIdConfig*);
+int LoadServiceModules(const char** dir_list, uint32_t instance_id, AppIdConfig*);
+
+// 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.
+int ReloadServiceModules(AppIdConfig*);
+
+int serviceLoadCallback(void* symbol);
+int serviceLoadForConfigCallback(void* symbol, AppIdConfig*);
+int ServiceAddPort(
+ RNAServiceValidationPort*, RNAServiceValidationModule*, Detector*, AppIdConfig*);
+void ServiceRemovePorts(RNAServiceValidationFCN, Detector*, AppIdConfig*);
+void ServiceRegisterPatternDetector(
+ RNAServiceValidationFCN, IpProtocol proto, const uint8_t* pattern,
+ unsigned size, int position, Detector*, const char* name);
+int AppIdDiscoverService(
+ Packet*, int direction, AppIdData*, const AppIdConfig*);
+AppId getPortServiceId(IpProtocol proto, uint16_t port, const AppIdConfig*);
+
+void AppIdFreeServiceIDState(AppIdServiceIDState*);
+
+int AppIdServiceAddService(
+ AppIdData*, const Packet*, int dir, const RNAServiceElement*,
+ AppId appId, const char* vendor, const char* version, const RNAServiceSubtype*);
+
+int AppIdServiceAddServiceSubtype(
+ AppIdData*, const Packet*, int dir, const RNAServiceElement*, AppId,
+ const char* vendor, const char* version, RNAServiceSubtype*);
+
+int AppIdServiceInProcess(
+ AppIdData*, const Packet*, int dir, const RNAServiceElement*);
+
+int AppIdServiceIncompatibleData(
+ AppIdData*, const Packet*, int dir, const RNAServiceElement*,
+ unsigned flow_data_index, const AppIdConfig*);
+
+int AppIdServiceFailService(
+ AppIdData*, const Packet*, int dir, const RNAServiceElement*,
+ unsigned flow_data_index, const AppIdConfig*);
+
+int AddFTPServiceState(AppIdData*);
+void AppIdFreeDhcpInfo(DHCPInfo*);
+void AppIdFreeSMBData(FpSMBData*);
+void AppIdFreeDhcpData(DhcpFPData*);
+
+void dumpPorts(FILE*, const AppIdConfig*);
+
+const RNAServiceElement* ServiceGetServiceElement(
+ RNAServiceValidationFCN, Detector*, AppIdConfig*);
+
+extern RNAServiceValidationModule* active_service_list;
+
+extern uint32_t app_id_instance_id;
+
+void cleanupFreeServiceMatch();
+void AppIdFreeServiceMatchList(ServiceMatch* sm);
+
+inline bool compareServiceElements(
+ const RNAServiceElement* first, const RNAServiceElement* second)
+{
+ if (first == second)
+ return 0;
+ if (first == nullptr || second == nullptr)
+ return 1;
+ return (first->validate != second->validate || first->userdata != second->userdata);
+}
+
+inline uint32_t AppIdServiceDetectionLevel(AppIdData* session)
+{
+ if (getAppIdFlag(session, APPID_SESSION_DECRYPTED))
+ return 1;
+ return 0;
+}
+
+inline void PopulateExpectedFlow(AppIdData* parent, AppIdData* expected, uint64_t flags)
+{
+ 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));
+ expected->rnaServiceState = RNA_STATE_FINISHED;
+ expected->rnaClientState = RNA_STATE_FINISHED;
+}
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_battle_field.cc author Sourcefire Inc.
+
+#include "service_battle_field.h"
+#include "application_ids.h"
+
+#include "main/snort_debug.h"
+#include "utils/util.h"
+
+enum CONNECTION_STATES
+{
+ CONN_STATE_INIT,
+ CONN_STATE_HELLO_DETECTED,
+ CONN_STATE_SERVICE_DETECTED,
+ CONN_STATE_MESSAGE_DETECTED,
+ CONN_STATE_MAX
+};
+
+static const unsigned MAX_PACKET_INSPECTION_COUNT = 10;
+
+struct ServiceData
+{
+ uint32_t state;
+ uint32_t messageId;
+ uint32_t packetCount;
+};
+
+static int battle_field_init(const IniServiceAPI* const init_api);
+static int battle_field_validate(ServiceValidationArgs* args);
+
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &battle_field_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "battle_field"
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &battle_field_validate, 4711, IpProtocol::TCP, 0 },
+ { &battle_field_validate, 16567, IpProtocol::UDP, 0 },
+ { &battle_field_validate, 27900, IpProtocol::UDP, 0 },
+ { &battle_field_validate, 27900, IpProtocol::TCP, 0 },
+ { &battle_field_validate, 29900, IpProtocol::UDP, 0 },
+ { &battle_field_validate, 29900, IpProtocol::TCP, 0 },
+ { &battle_field_validate, 27901, IpProtocol::TCP, 0 },
+ { &battle_field_validate, 28910, IpProtocol::UDP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+static const char PATTERN_HELLO[] = "battlefield2\x00";
+static const char PATTERN_2[] = "\xfe\xfd";
+static const char PATTERN_3[] = "\x11\x20\x00\x01\x00\x00\x50\xb9\x10\x11";
+static const char PATTERN_4[] = "\x11\x20\x00\x01\x00\x00\x30\xb9\x10\x11";
+static const char PATTERN_5[] = "\x11\x20\x00\x01\x00\x00\xa0\x98\x00\x11";
+static const char PATTERN_6[] = "\xfe\xfd\x09\x00\x00\x00\x00";
+
+RNAServiceValidationModule battlefield_service_mod =
+{
+ "BattleField",
+ &battle_field_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_BATTLEFIELD, 0 }
+};
+
+static int battle_field_init(const IniServiceAPI* const init_api)
+{
+ init_api->RegisterPattern(&battle_field_validate, IpProtocol::TCP, (uint8_t*)PATTERN_HELLO,
+ sizeof(PATTERN_HELLO)-1, 5, "battle_field", init_api->pAppidConfig);
+ init_api->RegisterPattern(&battle_field_validate, IpProtocol::TCP, (uint8_t*)PATTERN_2,
+ sizeof(PATTERN_2)-1, 0, "battle_field", init_api->pAppidConfig);
+ init_api->RegisterPattern(&battle_field_validate, IpProtocol::TCP, (uint8_t*)PATTERN_3,
+ sizeof(PATTERN_3)-1, 0, "battle_field", init_api->pAppidConfig);
+ init_api->RegisterPattern(&battle_field_validate, IpProtocol::TCP, (uint8_t*)PATTERN_4,
+ sizeof(PATTERN_4)-1, 0, "battle_field", init_api->pAppidConfig);
+ init_api->RegisterPattern(&battle_field_validate, IpProtocol::TCP, (uint8_t*)PATTERN_5,
+ sizeof(PATTERN_5)-1, 0, "battle_field", init_api->pAppidConfig);
+ init_api->RegisterPattern(&battle_field_validate, IpProtocol::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++)
+ {
+ DebugFormat(DEBUG_INSPECTOR,"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)
+{
+ ServiceData* fd;
+ AppIdData* flowp = args->flowp;
+ const uint8_t* data = args->data;
+ Packet* pkt = args->pkt;
+ uint16_t size = args->size;
+
+ if (!size)
+ {
+ goto inprocess_nofd;
+ }
+
+ fd = (ServiceData*)battlefield_service_mod.api->data_get(flowp,
+ battlefield_service_mod.flow_data_index);
+ if (!fd)
+ {
+ fd = (ServiceData*)snort_calloc(sizeof(ServiceData));
+ battlefield_service_mod.api->data_add(flowp, fd,
+ battlefield_service_mod.flow_data_index, &snort_free);
+ }
+
+ switch (fd->state)
+ {
+ case CONN_STATE_INIT:
+ if ((pkt->ptrs.sp >= 27000 || pkt->ptrs.dp >= 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);
+ 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);
+ 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, nullptr, nullptr, nullptr);
+ return SERVICE_SUCCESS;
+
+fail:
+ battlefield_service_mod.api->fail_service(flowp, pkt, args->dir, &svc_element,
+ battlefield_service_mod.flow_data_index,
+ args->pConfig);
+ return SERVICE_NOMATCH;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_battle_field.h author Sourcefire Inc.
+
+#ifndef SERVICE_BATTLEFIELD_H
+#define SERVICE_BATTLEFIELD_H
+
+#include "service_api.h"
+
+extern RNAServiceValidationModule battlefield_service_mod;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_bgp.cc author Sourcefire Inc.
+
+#include "service_bgp.h"
+#include "application_ids.h"
+
+#include "main/snort_debug.h"
+#include "utils/util.h"
+
+static const unsigned BGP_PORT = 179;
+
+static const unsigned BGP_V1_TYPE_OPEN = 1;
+static const unsigned BGP_V1_TYPE_OPEN_CONFIRM = 5;
+static const unsigned BGP_TYPE_OPEN = 1;
+static const unsigned BGP_TYPE_KEEPALIVE = 4;
+
+static const unsigned BGP_OPEN_LINK_MAX = 3;
+
+static const unsigned BGP_VERSION_MAX = 4;
+static const unsigned BGP_VERSION_MIN = 2;
+
+enum BGPState
+{
+ BGP_STATE_CONNECTION,
+ BGP_STATE_OPENSENT
+};
+
+#pragma pack(1)
+
+struct ServiceBGPData
+{
+ BGPState state;
+ int v1;
+};
+
+union ServiceBGPHeader
+{
+ 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;
+};
+
+struct ServiceBGPOpen
+{
+ uint8_t version;
+ uint16_t as;
+ uint16_t holdtime;
+};
+
+struct ServiceBGPV1Open
+{
+ uint16_t system;
+ uint8_t link;
+ uint8_t auth;
+};
+
+#pragma pack()
+
+static int bgp_init(const IniServiceAPI* const init_api);
+static int bgp_validate(ServiceValidationArgs* args);
+
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &bgp_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "bgp"
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &bgp_validate, BGP_PORT, IpProtocol::TCP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+RNAServiceValidationModule bgp_service_mod =
+{
+ "BGP",
+ &bgp_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static uint8_t BGP_PATTERN[] =
+{
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_BGP, 0 }
+};
+
+static int bgp_init(const IniServiceAPI* const init_api)
+{
+ init_api->RegisterPattern(&bgp_validate, IpProtocol::TCP, BGP_PATTERN, sizeof(BGP_PATTERN), 0,
+ "bgp", init_api->pAppidConfig);
+ unsigned i;
+ for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++)
+ {
+ DebugFormat(DEBUG_INSPECTOR,"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;
+ AppIdData* 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 = (ServiceBGPData*)bgp_service_mod.api->data_get(flowp, bgp_service_mod.flow_data_index);
+ if (!bd)
+ {
+ bd = (ServiceBGPData*)snort_calloc(sizeof(ServiceBGPData));
+ bgp_service_mod.api->data_add(flowp, bd, bgp_service_mod.flow_data_index, &snort_free);
+ 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);
+ 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);
+ return SERVICE_NOMATCH;
+
+success:
+ bgp_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element,
+ APP_ID_BGP, nullptr, nullptr, nullptr);
+ return SERVICE_SUCCESS;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_bgp.h author Sourcefire Inc.
+
+#ifndef SERVICE_BGP_H
+#define SERVICE_BGP_H
+
+#include "service_api.h"
+
+extern RNAServiceValidationModule bgp_service_mod;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_bit.cc author Sourcefire Inc.
+
+#include "main/snort_debug.h"
+#include "utils/util.h"
+#include "application_ids.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)
+
+enum BITState
+{
+ BIT_STATE_BANNER,
+ BIT_STATE_BANNER_DC,
+ BIT_STATE_MESSAGE_LEN,
+ BIT_STATE_MESSAGE_DATA
+};
+
+struct ServiceBITData
+{
+ BITState state;
+ unsigned stringlen;
+ unsigned pos;
+ union
+ {
+ uint32_t len;
+ uint8_t raw_len[4];
+ } l;
+};
+
+#pragma pack(1)
+struct ServiceBITMsg
+{
+ uint32_t len;
+ uint8_t code;
+};
+#pragma pack()
+
+static int bit_init(const IniServiceAPI* const init_api);
+static int bit_validate(ServiceValidationArgs* args);
+
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &bit_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "bit"
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &bit_validate, BIT_PORT, IpProtocol::TCP, 0 },
+ { &bit_validate, BIT_PORT+1, IpProtocol::TCP, 0 },
+ { &bit_validate, BIT_PORT+2, IpProtocol::TCP, 0 },
+ { &bit_validate, BIT_PORT+3, IpProtocol::TCP, 0 },
+ { &bit_validate, BIT_PORT+4, IpProtocol::TCP, 0 },
+ { &bit_validate, BIT_PORT+5, IpProtocol::TCP, 0 },
+ { &bit_validate, BIT_PORT+6, IpProtocol::TCP, 0 },
+ { &bit_validate, BIT_PORT+7, IpProtocol::TCP, 0 },
+ { &bit_validate, BIT_PORT+8, IpProtocol::TCP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+// FIXIT - Why is there no service_bit.h that declares this extern like all the others?
+SO_PUBLIC RNAServiceValidationModule bit_service_mod =
+{
+ svc_name,
+ &bit_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_BITTORRENT, 0 }
+};
+
+static int bit_init(const IniServiceAPI* const init_api)
+{
+ init_api->RegisterPattern(&bit_validate, IpProtocol::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++)
+ {
+ DebugFormat(DEBUG_INSPECTOR,"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;
+ AppIdData* 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 = (ServiceBITData*)bit_service_mod.api->data_get(flowp, bit_service_mod.flow_data_index);
+ if (!ss)
+ {
+ ss = (ServiceBITData*)snort_calloc(sizeof(ServiceBITData));
+ bit_service_mod.api->data_add(flowp, ss, bit_service_mod.flow_data_index, &snort_free);
+ 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);
+ return SERVICE_INPROCESS;
+
+success:
+ bit_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element,
+ APP_ID_BITTORRENT, nullptr, nullptr, nullptr);
+ 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);
+ return SERVICE_NOMATCH;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_bootp.cc author Sourcefire Inc.
+
+#include "service_bootp.h"
+#include "main/snort_debug.h"
+#include "protocols/eth.h"
+#include "app_info_table.h"
+#include "application_ids.h"
+
+#define DHCP_MAGIC_COOKIE 0x63825363
+
+#pragma pack(1)
+
+struct ServiceBOOTPHeader
+{
+ 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];
+};
+
+enum DHCP_OPTIONS
+{
+ 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
+};
+
+struct ServiceDHCPOption
+{
+ uint8_t option;
+ uint8_t len;
+};
+
+#pragma pack()
+
+static int bootp_init(const IniServiceAPI* const init_api);
+static int bootp_validate(ServiceValidationArgs* args);
+
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &bootp_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "bootp"
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &bootp_validate, 67, IpProtocol::UDP, 0 },
+ { &bootp_validate, 67, IpProtocol::UDP, 1 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+RNAServiceValidationModule bootp_service_mod =
+{
+ "DHCP",
+ &bootp_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_DHCP, APPINFO_FLAG_SERVICE_ADDITIONAL | APPINFO_FLAG_SERVICE_UDP_REVERSED }
+};
+
+static int bootp_init(const IniServiceAPI* const init_api)
+{
+ unsigned i;
+ for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++)
+ {
+ DebugFormat(DEBUG_INSPECTOR,"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=nullptr;
+ const uint8_t* op60=nullptr;
+ AppIdData* flowp = args->flowp;
+ const uint8_t* data = args->data;
+ Packet* 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; i<sizeof(bh->sname); i++)
+ {
+ if (!bh->sname[i])
+ break;
+ }
+ if (i >= sizeof(bh->sname))
+ goto fail;
+
+ for (i=0; i<sizeof(bh->file); 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); i<size; )
+ {
+ op = (ServiceDHCPOption*)&data[i];
+ if (op->option == 0xff)
+ {
+ const eth::EtherHdr* eh = layer::get_eth_layer(pkt);
+
+ if (!eh)
+ goto fail;
+
+ if (option53 && op55_len && (memcmp(eh->ether_src, 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);
+ i<size;
+ )
+ {
+ op = (ServiceDHCPOption*)&data[i];
+ if (op->option == 0xff)
+ {
+ const eth::EtherHdr* eh = layer::get_eth_layer(pkt);
+
+ if (!eh)
+ goto fail;
+
+ if (option53 && (memcmp(eh->ether_dst, bh->chaddr, 6) == 0))
+ bootp_service_mod.api->dhcpNewLease(flowp, bh->chaddr, bh->yiaddr,
+ pkt->pkth->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, nullptr, nullptr, nullptr);
+ }
+ return SERVICE_SUCCESS;
+
+inprocess:
+ if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED))
+ {
+ bootp_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &svc_element);
+ }
+ 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);
+ }
+ 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);
+ }
+ return SERVICE_NOT_COMPATIBLE;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_bootp.h author Sourcefire Inc.
+
+#ifndef SERVICE_BOOTP_H
+#define SERVICE_BOOTP_H
+
+// Service detector for BOOTP
+
+#include "service_api.h"
+
+extern RNAServiceValidationModule bootp_service_mod;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_config.h author Sourcefire Inc.
+
+#ifndef SERVICE_CONFIG_H
+#define SERVICE_CONFIG_H
+
+// Service detector configuration
+
+#include <cstdint>
+
+#include <appid.h>
+#include "service_api.h"
+
+#define RNA_SERVICE_MAX_PORT 65536
+
+struct RNAServiceElement;
+struct RNAServiceValidationModule;
+class SearchTool;
+
+struct SSLCertPattern
+{
+ uint8_t type;
+ AppId appId;
+ uint8_t* pattern;
+ int pattern_size;
+};
+
+struct DetectorSSLCertPattern
+{
+ SSLCertPattern* dpattern;
+ DetectorSSLCertPattern* next;
+};
+
+struct ServiceSslConfig
+{
+ DetectorSSLCertPattern* DetectorSSLCertPatternList;
+ DetectorSSLCertPattern* DetectorSSLCnamePatternList;
+ SearchTool* ssl_host_matcher;
+ SearchTool* ssl_cname_matcher;
+};
+
+// DNS host pattern structure
+struct DNSHostPattern
+{
+ uint8_t type;
+ AppId appId;
+ uint8_t* pattern;
+ int pattern_size;
+};
+
+struct DetectorDNSHostPattern
+{
+ DNSHostPattern* dpattern;
+ DetectorDNSHostPattern* next;
+};
+
+struct ServiceDnsConfig
+{
+ DetectorDNSHostPattern* DetectorDNSHostPatternList;
+ SearchTool* dns_host_host_matcher;
+};
+
+struct ServicePatternData
+{
+ ServicePatternData* next;
+ int position;
+ unsigned size;
+ RNAServiceElement* svc;
+};
+
+struct ServiceConfig
+{
+ RNAServiceValidationModule* active_service_list; // List of all services (Lua and C)
+ RNAServiceElement* tcp_service_list; // List of all TCP services (Lua and C)
+ RNAServiceElement* udp_service_list; // List of all UDP services (Lua and C)
+ 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];
+
+ SearchTool* tcp_patterns;
+ ServicePatternData* tcp_pattern_data;
+ int tcp_pattern_count;
+ SearchTool* udp_patterns;
+ ServicePatternData* udp_pattern_data;
+ int udp_pattern_count;
+};
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_dcerpc.cc author Sourcefire Inc.
+
+#include "service_dcerpc.h"
+#include "application_ids.h"
+#include "dcerpc.h"
+
+#include "main/snort_debug.h"
+#include "utils/util.h"
+
+#define DCERPC_THRESHOLD 3
+
+#define min(x,y) ((x)<(y) ? (x) : (y))
+
+struct ServiceDCERPCData
+{
+ unsigned count;
+};
+
+static int dcerpc_init(const IniServiceAPI* const init_api);
+static int dcerpc_tcp_validate(ServiceValidationArgs* args);
+static int dcerpc_udp_validate(ServiceValidationArgs* args);
+
+static RNAServiceElement tcp_svc_element =
+{
+ nullptr,
+ &dcerpc_tcp_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "dcerpc"
+};
+static RNAServiceElement udp_svc_element =
+{
+ nullptr,
+ &dcerpc_udp_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "udp dcerpc"
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &dcerpc_tcp_validate, 135, IpProtocol::TCP, 0 },
+ { &dcerpc_udp_validate, 135, IpProtocol::UDP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+RNAServiceValidationModule dcerpc_service_mod =
+{
+ "DCERPC",
+ &dcerpc_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_DCE_RPC, 0 }
+};
+
+static int dcerpc_init(const IniServiceAPI* const init_api)
+{
+ unsigned i;
+ for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++)
+ {
+ DebugFormat(DEBUG_INSPECTOR,"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;
+ AppIdData* 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 = (ServiceDCERPCData*)dcerpc_service_mod.api->data_get(flowp,
+ dcerpc_service_mod.flow_data_index);
+ if (!dd)
+ {
+ dd = (ServiceDCERPCData*)snort_calloc(sizeof(ServiceDCERPCData));
+ dcerpc_service_mod.api->data_add(flowp, dd, dcerpc_service_mod.flow_data_index,
+ &snort_free);
+ }
+
+ 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, nullptr, nullptr, nullptr);
+ return SERVICE_SUCCESS;
+ }
+
+inprocess:
+ dcerpc_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &tcp_svc_element);
+ 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);
+ return SERVICE_NOMATCH;
+}
+
+static int dcerpc_udp_validate(ServiceValidationArgs* args)
+{
+ ServiceDCERPCData* dd;
+ int retval = SERVICE_NOMATCH;
+ int length;
+ AppIdData* 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 = (ServiceDCERPCData*)dcerpc_service_mod.api->data_get(flowp,
+ dcerpc_service_mod.flow_data_index);
+ if (!dd)
+ {
+ dd = (ServiceDCERPCData*)snort_calloc(sizeof(ServiceDCERPCData));
+ dcerpc_service_mod.api->data_add(flowp, dd, dcerpc_service_mod.flow_data_index,
+ &snort_free);
+ }
+
+ 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, nullptr, nullptr, nullptr);
+ return SERVICE_SUCCESS;
+ }
+
+inprocess:
+ dcerpc_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &udp_svc_element);
+ 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);
+ return SERVICE_NOMATCH;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_dcerpc.h author Sourcefire Inc.
+
+#ifndef SERVICE_DCERPC_H
+#define SERVICE_DCERPC_H
+
+// Service detector for DCE/RPC
+
+#include "service_api.h"
+
+extern RNAServiceValidationModule dcerpc_service_mod;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_direct_connect.cc author Sourcefire Inc.
+
+#include "service_direct_connect.h"
+#include "application_ids.h"
+
+#include "main/snort_debug.h"
+#include "utils/util.h"
+
+enum CONNECTION_STATES
+{
+ CONN_STATE_INIT,
+ CONN_STATE_1,
+ CONN_STATE_2,
+ CONN_STATE_SERVICE_DETECTED,
+ CONN_STATE_MAX
+};
+
+#define MAX_PACKET_INSPECTION_COUNT 10
+
+struct ServiceData
+{
+ uint32_t state;
+ uint32_t packetCount;
+};
+
+static int direct_connect_init(const IniServiceAPI* const init_api);
+static int direct_connect_validate(ServiceValidationArgs* args);
+static int validateDirectConnectTcp(const uint8_t* data, uint16_t size, const int dir,
+ AppIdData* flowp, const Packet* pkt, ServiceData* serviceData,
+ const AppIdConfig* pConfig);
+static int validateDirectConnectUdp(const uint8_t* data, uint16_t size, const int dir,
+ AppIdData* flowp, const Packet* pkt, ServiceData* serviceData,
+ const AppIdConfig* pConfig);
+
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &direct_connect_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "direct_connect"
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &direct_connect_validate, 411, IpProtocol::TCP, 0 },
+ { &direct_connect_validate, 411, IpProtocol::UDP, 0 },
+ { &direct_connect_validate, 412, IpProtocol::TCP, 0 },
+ { &direct_connect_validate, 412, IpProtocol::UDP, 0 },
+ { &direct_connect_validate, 413, IpProtocol::TCP, 0 },
+ { &direct_connect_validate, 413, IpProtocol::UDP, 0 },
+ { &direct_connect_validate, 414, IpProtocol::TCP, 0 },
+ { &direct_connect_validate, 414, IpProtocol::UDP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 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 "
+
+RNAServiceValidationModule directconnect_service_mod =
+{
+ "DirectConnect",
+ &direct_connect_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_DIRECT_CONNECT, 0 }
+};
+
+static int direct_connect_init(const IniServiceAPI* const init_api)
+{
+ init_api->RegisterPattern(&direct_connect_validate, IpProtocol::TCP, (uint8_t*)PATTERN1,
+ sizeof(PATTERN1)-1, 0, "direct_connect", init_api->pAppidConfig);
+ init_api->RegisterPattern(&direct_connect_validate, IpProtocol::TCP, (uint8_t*)PATTERN2,
+ sizeof(PATTERN2)-1, 0, "direct_connect", init_api->pAppidConfig);
+ init_api->RegisterPattern(&direct_connect_validate, IpProtocol::TCP, (uint8_t*)PATTERN3,
+ sizeof(PATTERN3)-1, 0, "direct_connect", init_api->pAppidConfig);
+ init_api->RegisterPattern(&direct_connect_validate, IpProtocol::TCP, (uint8_t*)PATTERN4,
+ sizeof(PATTERN4)-1, 0, "direct_connect", init_api->pAppidConfig);
+ init_api->RegisterPattern(&direct_connect_validate, IpProtocol::TCP, (uint8_t*)PATTERN5,
+ sizeof(PATTERN5)-1, 0, "direct_connect", init_api->pAppidConfig);
+ init_api->RegisterPattern(&direct_connect_validate, IpProtocol::TCP, (uint8_t*)PATTERN6,
+ sizeof(PATTERN6)-1, 0, "direct_connect", init_api->pAppidConfig);
+ init_api->RegisterPattern(&direct_connect_validate, IpProtocol::UDP, (uint8_t*)PATTERN7,
+ sizeof(PATTERN7)-1, 0, "direct_connect", init_api->pAppidConfig);
+
+ unsigned i;
+ for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++)
+ {
+ DebugFormat(DEBUG_INSPECTOR,"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)
+{
+ ServiceData* fd;
+ AppIdData* 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);
+ return SERVICE_INPROCESS;
+ }
+
+ fd = (ServiceData*)directconnect_service_mod.api->data_get(flowp,
+ directconnect_service_mod.flow_data_index);
+ if (!fd)
+ {
+ fd = (ServiceData*)snort_calloc(sizeof(ServiceData));
+ directconnect_service_mod.api->data_add(flowp, fd,
+ directconnect_service_mod.flow_data_index, &snort_free);
+ }
+
+ if (flowp->proto == IpProtocol::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,
+ AppIdData* flowp, const Packet* pkt, ServiceData* serviceData,
+ const 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);
+ 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, nullptr, nullptr, nullptr);
+ return SERVICE_SUCCESS;
+
+fail:
+ directconnect_service_mod.api->fail_service(flowp, pkt, dir, &svc_element,
+ directconnect_service_mod.flow_data_index, pConfig);
+ return SERVICE_NOMATCH;
+}
+
+static int validateDirectConnectUdp(const uint8_t* data, uint16_t size, const int dir,
+ AppIdData* flowp, const Packet* pkt, ServiceData* serviceData,
+ const 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);
+ 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, nullptr, nullptr, nullptr);
+ return SERVICE_SUCCESS;
+
+fail:
+ directconnect_service_mod.api->fail_service(flowp, pkt, dir, &svc_element,
+ directconnect_service_mod.flow_data_index, pConfig);
+ return SERVICE_NOMATCH;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_direct_connect.h author Sourcefire Inc.
+
+#ifndef SERVICE_DIRECTCONNECT_H
+#define SERVICE_DIRECTCONNECT_H
+
+// Service detector for Direct Connect
+
+#include "service_api.h"
+
+extern RNAServiceValidationModule directconnect_service_mod;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_flap.cc author Sourcefire Inc.
+
+#include "service_flap.h"
+#include "application_ids.h"
+
+#include "main/snort_debug.h"
+#include "utils/util.h"
+
+#define FLAP_PORT 5190
+
+enum FLAPState
+{
+ FLAP_STATE_ACK,
+ FLAP_STATE_COOKIE
+};
+
+#define FNAC_SIGNON 0x0017
+#define FNAC_GENERIC 0x0001
+#define FNAC_SUB_SIGNON_REPLY 0x0007
+#define FNAC_SUB_SERVER_READY 0x0003
+
+struct ServiceFLAPData
+{
+ FLAPState state;
+};
+
+#pragma pack(1)
+
+struct FLAPFNACSignOn
+{
+ uint16_t len;
+};
+
+struct FLAPFNAC
+{
+ uint16_t family;
+ uint16_t subtype;
+ uint16_t flags;
+ uint32_t id;
+};
+
+struct FLAPTLV
+{
+ uint16_t subtype;
+ uint16_t len;
+};
+
+struct FLAPHeader
+{
+ uint8_t start;
+ uint8_t type;
+ uint16_t seq;
+ uint16_t len;
+};
+
+#pragma pack()
+
+static int flap_init(const IniServiceAPI* const init_api);
+static int flap_validate(ServiceValidationArgs* args);
+
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &flap_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "flap"
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &flap_validate, 5190, IpProtocol::TCP, 0 },
+ { &flap_validate, 9898, IpProtocol::TCP, 0 },
+ { &flap_validate, 4443, IpProtocol::TCP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+RNAServiceValidationModule flap_service_mod =
+{
+ "FLAP",
+ &flap_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static uint8_t FLAP_PATTERN[] = { 0x2A, 0x01 };
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_AOL_INSTANT_MESSENGER, 0 }
+};
+
+static int flap_init(const IniServiceAPI* const init_api)
+{
+ init_api->RegisterPattern(&flap_validate, IpProtocol::TCP, FLAP_PATTERN,
+ sizeof(FLAP_PATTERN), 0, "flap", init_api->pAppidConfig);
+ //unsigned i;
+ for (unsigned i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++)
+ {
+ DebugFormat(DEBUG_INSPECTOR,"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;
+ AppIdData* 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 = (ServiceFLAPData*)flap_service_mod.api->data_get(flowp, flap_service_mod.flow_data_index);
+ if (!sf)
+ {
+ sf = (ServiceFLAPData*)snort_calloc(sizeof(ServiceFLAPData));
+ flap_service_mod.api->data_add(flowp, sf, flap_service_mod.flow_data_index, &snort_free);
+ 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);
+ return SERVICE_NOMATCH;
+
+success:
+ flap_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element,
+ APP_ID_AOL_INSTANT_MESSENGER, nullptr, nullptr, nullptr);
+ return SERVICE_SUCCESS;
+
+inprocess:
+ flap_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &svc_element);
+ return SERVICE_INPROCESS;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_flap.h author Sourcefire Inc.
+
+#ifndef SERVICE_FLAP_H
+#define SERVICE_FLAP_H
+
+// Service detector for FLAP
+
+#include "service_api.h"
+
+extern RNAServiceValidationModule flap_service_mod;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_ftp.cc author Sourcefire Inc.
+
+#include "service_ftp.h"
+
+#include "main/snort_debug.h"
+#include "sfip/sf_ip.h"
+#include "target_based/snort_protocols.h"
+#include "utils/util.h"
+
+#include "app_info_table.h"
+#include "appid_api.h"
+#include "appid_flow_data.h"
+#include "application_ids.h"
+#include "service_base.h"
+#include "service_util.h"
+
+// FIXIT-H This needs to use a real SFIP function
+static SFIP_RET sfip_convert_ip_text_to_binary(const int, const char*, void*)
+{ return SFIP_SUCCESS; }
+
+#define FTP_PORT 21
+/*#define RNA_FTP_EXPECTED_ON_PORT 1 */
+
+enum FTPState
+{
+ FTP_STATE_CONNECTION,
+ FTP_STATE_LOGIN,
+ FTP_STATE_PASSWORD,
+ FTP_STATE_ACCOUNT,
+ FTP_STATE_CONNECTION_ERROR,
+ FTP_STATE_MONITOR
+};
+
+enum FTPReplyState
+{
+ FTP_REPLY_BEGIN,
+ FTP_REPLY_MULTI,
+ FTP_REPLY_MID
+};
+
+enum FTPCmd
+{
+ FTP_CMD_NONE,
+ FTP_CMD_PORT_EPRT,
+ FTP_CMD_PASV_EPSV
+};
+
+#define MAX_STRING_SIZE 64
+struct ServiceFTPData
+{
+ FTPState state;
+ FTPReplyState rstate;
+ int code;
+ char vendor[MAX_STRING_SIZE];
+ char version[MAX_STRING_SIZE];
+ FTPCmd cmd;
+ sfip_t address;
+ uint16_t port;
+};
+
+#pragma pack(1)
+
+struct ServiceFTPCode
+{
+ uint8_t code[3];
+ uint8_t sp;
+};
+
+#pragma pack()
+
+static int ftp_init(const IniServiceAPI* const init_api);
+static int ftp_validate(ServiceValidationArgs* args);
+
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &ftp_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "ftp"
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &ftp_validate, FTP_PORT, IpProtocol::TCP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+RNAServiceValidationModule ftp_service_mod =
+{
+ "FTP",
+ &ftp_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+#define FTP_PATTERN1 "220 "
+#define FTP_PATTERN2 "220-"
+#define FTP_PATTERN3 "FTP"
+#define FTP_PATTERN4 "ftp"
+
+static AppRegistryEntry 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 IniServiceAPI* const init_api)
+{
+ ftp_data_app_id = AddProtocolReference("ftp-data");
+
+ init_api->RegisterPattern(&ftp_validate, IpProtocol::TCP, (uint8_t*)FTP_PATTERN1,
+ sizeof(FTP_PATTERN1)-1, 0, "ftp", init_api->pAppidConfig);
+ init_api->RegisterPattern(&ftp_validate, IpProtocol::TCP, (uint8_t*)FTP_PATTERN2,
+ sizeof(FTP_PATTERN2)-1, 0, "ftp", init_api->pAppidConfig);
+ init_api->RegisterPattern(&ftp_validate, IpProtocol::TCP, (uint8_t*)FTP_PATTERN3,
+ sizeof(FTP_PATTERN3)-1, -1, "ftp", init_api->pAppidConfig);
+ init_api->RegisterPattern(&ftp_validate, IpProtocol::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++)
+ {
+ DebugFormat(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';
+}
+
+enum VVP_PARSE_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
+};
+
+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,
+ (const uint8_t*)" ", sizeof(" ")-1,
+ VVP_PARSE_PRO_FTPD);
+ case VVP_PARSE_PURE_FTPD:
+ return VendorVersionParse(data, init_offset, offset,fd,
+ ven_pureftpd, sizeof(ven_pureftpd)-1,
+ nullptr, 0,
+ VVP_PARSE_PURE_FTPD);
+ case VVP_PARSE_NC_FTPD:
+ return VendorVersionParse(data, init_offset, offset,fd,
+ ven_ncftpd, sizeof(ven_ncftpd)-1,
+ nullptr, 0,
+ VVP_PARSE_NC_FTPD);
+ }
+ 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 (; *offset<size; (*offset)++)
+ {
+ if (data[*offset] != 0x0D && data[*offset] != 0x0A)
+ break;
+ }
+
+ switch (fd->rstate)
+ {
+ 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 && *p!='('; p++)
+ ;
+ if (p < end)
+ {
+ p++;
+ ven = p;
+ for (; p<end && *p && *p!=' '; p++)
+ ;
+ if (p < end && *p)
+ {
+ CopyVendorString(fd, ven, p-ven);
+ ver = p + 1;
+ for (p=ver; p<end && *p && *p!=':'; p++)
+ ;
+ if (p>=end || !(*p))
+ {
+ for (p=ver; p<end && *p && *p!=')'; p++)
+ ;
+ }
+ if (p < end && *p)
+ {
+ CopyVersionString(fd, ver, p-ver);
+ }
+ }
+ }
+ }
+ }
+ else if (fd->code == 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;
+ 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 = tmp_state;
+ break;
+ }
+ if (data[*offset] == 0x0A)
+ {
+ fd->rstate = tmp_state;
+ break;
+ }
+ }
+ if (fd->rstate == FTP_REPLY_MID)
+ return -1;
+ break;
+ case FTP_REPLY_MULTI:
+ if (size - *offset < (int)sizeof(ServiceFTPCode))
+ {
+ fd->rstate = FTP_REPLY_MID;
+ 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 = FTP_REPLY_MULTI;
+ break;
+ }
+ if (data[*offset] == 0x0A)
+ {
+ fd->rstate = FTP_REPLY_MULTI;
+ break;
+ }
+ }
+ if (fd->rstate == FTP_REPLY_MID)
+ return -1;
+ }
+ else
+ {
+ code_hdr = (ServiceFTPCode*)(data + *offset);
+ if (size - (*offset) >= (int)sizeof(ServiceFTPCode) &&
+ (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;
+ 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 = tmp_state;
+ break;
+ }
+ if (data[*offset] == 0x0A)
+ {
+ fd->rstate = tmp_state;
+ break;
+ }
+ }
+ if (fd->rstate == FTP_REPLY_MID)
+ return -1;
+ }
+ 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 && *data!='('; data++)
+ ;
+ data++;
+ if (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 && *data!='('; data++)
+ ;
+ data++;
+ if (data >= end)
+ return 1;
+
+ delimiter = *data++;
+ if (data >= end)
+ return 1;
+
+ for (; data<end && *data!=delimiter; data++)
+ ;
+ data++;
+ if (data >= end)
+ return 1;
+
+ for (; data<end && *data!=delimiter; data++)
+ ;
+ data++;
+ if (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,
+ sfip_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(sfip_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 */
+struct addr_family_map
+{
+ uint16_t eprt_fam;
+ uint16_t sfaddr_fam;
+};
+
+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,
+ sfip_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(sfip_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) != 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, AppIdData* 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 InitializeDataSession(AppIdData* flowp,AppIdData* fp)
+{
+ 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);
+}
+
+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;
+ AppIdData* fp;
+ int retval = SERVICE_INPROCESS;
+ AppIdData* flowp = args->flowp;
+ const uint8_t* data = args->data;
+ Packet* 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 = (ServiceFTPData*)ftp_service_mod.api->data_get(flowp, ftp_service_mod.flow_data_index);
+ if (!fd)
+ {
+ fd = (ServiceFTPData*)snort_calloc(sizeof(ServiceFTPData));
+ ftp_service_mod.api->data_add(flowp, fd, ftp_service_mod.flow_data_index, &snort_free);
+ 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)
+ {
+ 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)
+ {
+ 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:
+ {
+ setAppIdFlag(flowp, APPID_SESSION_CONTINUE);
+ 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:
+ fd->state = FTP_STATE_PASSWORD;
+ 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)
+ {
+ sfip_t ip;
+ const sfip_t* sip;
+ const sfip_t* dip;
+ uint32_t addr;
+
+ dip = pkt->ptrs.ip_api.get_dst();
+ sip = pkt->ptrs.ip_api.get_src();
+ addr = htonl(address);
+ sfip_set_raw(&ip, &addr, AF_INET);
+ fp = ftp_service_mod.api->flow_new(flowp, pkt, dip, 0, &ip, port, flowp->proto,
+ ftp_data_app_id,
+ APPID_EARLY_SESSION_FLAG_FW_RULE);
+ if (fp)
+ {
+ InitializeDataSession(flowp,fp);
+ }
+ if (!sfip_fast_eq6(&ip, sip))
+ {
+ fp = ftp_service_mod.api->flow_new(flowp, pkt, dip, 0, sip, port,
+ flowp->proto, ftp_data_app_id,
+ APPID_EARLY_SESSION_FLAG_FW_RULE);
+ if (fp)
+ {
+ InitializeDataSession(flowp,fp);
+ }
+ }
+ 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)
+ {
+ const sfip_t* sip;
+ const sfip_t* dip;
+ dip = pkt->ptrs.ip_api.get_dst();
+ sip = pkt->ptrs.ip_api.get_src();
+ fp = ftp_service_mod.api->flow_new(flowp, pkt, dip, 0, sip, port, flowp->proto,
+ ftp_data_app_id,
+ APPID_EARLY_SESSION_FLAG_FW_RULE);
+ if (fp)
+ {
+ InitializeDataSession(flowp,fp);
+ }
+ 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)
+ {
+ const sfip_t* sip;
+ sip = pkt->ptrs.ip_api.get_src();
+ fp = ftp_service_mod.api->flow_new(flowp, pkt, sip, 0, &fd->address, fd->port,
+ flowp->proto, ftp_data_app_id,
+ APPID_EARLY_SESSION_FLAG_FW_RULE);
+ if (fp)
+ {
+ InitializeDataSession(flowp,fp);
+ }
+ 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);
+ }
+ 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 : nullptr,
+ fd->version[0] ? fd->version : nullptr, nullptr);
+ }
+ 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);
+ }
+ clearAppIdFlag(flowp, APPID_SESSION_CONTINUE);
+ return SERVICE_NOMATCH;
+ }
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_ftp.h author Sourcefire Inc.
+
+#ifndef SERVICE_FTP_H
+#define SERVICE_FTP_H
+
+// Service detector for FTP
+
+#include "service_api.h"
+
+extern RNAServiceValidationModule ftp_service_mod;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_irc.cc author Sourcefire Inc.
+
+#include "service_irc.h"
+
+#include "main/snort_debug.h"
+#include "utils/util.h"
+
+#include "appid_flow_data.h"
+#include "application_ids.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 ";
+
+enum IRCState
+{
+ 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
+};
+
+struct ServiceIRCData
+{
+ IRCState state;
+ unsigned pos;
+ const char* command;
+ IRCState initiator_state;
+ unsigned initiator_pos;
+ const char* initiator_command;
+ unsigned count;
+};
+
+static int irc_init(const IniServiceAPI* const init_api);
+static int irc_validate(ServiceValidationArgs* args);
+
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &irc_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "irc"
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &irc_validate, 6667, IpProtocol::TCP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+RNAServiceValidationModule irc_service_mod =
+{
+ "IRC",
+ &irc_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_IRCD, 0 }
+};
+
+static int irc_init(const IniServiceAPI* const init_api)
+{
+ unsigned i;
+ for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++)
+ {
+ DebugFormat(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;
+ AppIdData* flowp = args->flowp;
+ const uint8_t* data = args->data;
+ const int dir = args->dir;
+ uint16_t size = args->size;
+
+ if (!size)
+ goto inprocess;
+
+ id = (ServiceIRCData*)irc_service_mod.api->data_get(flowp, irc_service_mod.flow_data_index);
+ if (!id)
+ {
+ id = (ServiceIRCData*)snort_calloc(sizeof(ServiceIRCData));
+ irc_service_mod.api->data_add(flowp, id, irc_service_mod.flow_data_index, &snort_free);
+ 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 (; data<end; data++)
+ {
+ switch (*state)
+ {
+ case IRC_STATE_BEGIN:
+ if (*data == ':')
+ {
+ *state = IRC_STATE_MID_PREFIX;
+ break;
+ }
+ /* Fall through */
+ case IRC_STATE_COMMAND_BEGIN:
+ if (*data == ' ')
+ break;
+ else if (isdigit(*data))
+ {
+ *state = IRC_STATE_MID_NUMERIC_COMMAND;
+ *pos = 1;
+ break;
+ }
+ else
+ {
+ if (dir == APP_ID_FROM_RESPONDER)
+ {
+ if (*data == IRC_NOTICE[0])
+ *command = IRC_NOTICE;
+ else if (*data == IRC_ERROR[0])
+ *command = IRC_ERROR;
+ else if (*data == IRC_PONG[0])
+ *command = IRC_PONG;
+ else
+ goto fail;
+ }
+ else
+ {
+ if (*data == IRC_USER[0])
+ *command = IRC_USER;
+ else
+ {
+ *state = IRC_STATE_LINE;
+ break;
+ }
+ }
+ }
+
+ *pos = 1;
+ *state = IRC_STATE_MID_COMMAND;
+ break;
+ case IRC_STATE_MID_COMMAND:
+ if (*data != (*command)[*pos])
+ {
+ if (*command == IRC_PONG && *pos == 1 && *data == IRC_PING[1])
+ {
+ *command = IRC_PING;
+ }
+ else
+ goto fail;
+ }
+ (*pos)++;
+ if (!(*command)[*pos])
+ {
+ if (dir == APP_ID_FROM_RESPONDER)
+ {
+ *state = IRC_STATE_LINE;
+ }
+ else
+ {
+ *state = IRC_STATE_USER_USERNAME;
+ }
+ }
+ break;
+ case IRC_STATE_LINE:
+ if (*data == 0x0D)
+ *state = IRC_STATE_MID_TERM;
+ else if (*data == 0x0A)
+ {
+ *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_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);
+ return SERVICE_INPROCESS;
+
+success:
+ irc_service_mod.api->add_service(flowp, args->pkt, dir, &svc_element,
+ APP_ID_IRCD, nullptr, nullptr, nullptr);
+ 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);
+ }
+ else
+ {
+ irc_service_mod.api->incompatible_data(flowp, args->pkt, dir, &svc_element,
+ irc_service_mod.flow_data_index, args->pConfig);
+ }
+ return SERVICE_NOMATCH;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_irc.h author Sourcefire Inc.
+
+#ifndef SERVICE_IRC_H
+#define SERVICE_IRC_H
+
+// Service detector for IRC
+
+#include "service_api.h"
+
+extern RNAServiceValidationModule irc_service_mod;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_lpr.cc author Sourcefire Inc.
+
+#include "main/snort_debug.h"
+#include "utils/util.h"
+
+#include "application_ids.h"
+#include "appid_flow_data.h"
+#include "service_api.h"
+
+#define LPR_COUNT_THRESHOLD 5
+
+enum LPRState
+{
+ LPR_STATE_COMMAND,
+ LPR_STATE_RECEIVE,
+ LPR_STATE_REPLY1,
+ LPR_STATE_REPLY,
+ LPR_STATE_IGNORE
+};
+
+enum LPRCommand
+{
+ LPR_CMD_PRINT = 1,
+ LPR_CMD_RECEIVE,
+ LPR_CMD_SHORT_STATE,
+ LPR_CMD_LONG_STATE,
+ LPR_CMD_REMOVE
+};
+
+enum LPRSubCommand
+{
+ LPR_SUBCMD_ABORT = 1,
+ LPR_SUBCMD_CONTROL,
+ LPR_SUBCMD_DATA
+};
+
+struct ServiceLPRData
+{
+ LPRState state;
+ unsigned no_data_count;
+ unsigned count;
+};
+
+static int lpr_init(const IniServiceAPI* const init_api);
+static int lpr_validate(ServiceValidationArgs* args);
+
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &lpr_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "lpr"
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &lpr_validate, 515, IpProtocol::TCP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+RNAServiceValidationModule lpr_service_mod =
+{
+ "LPR",
+ &lpr_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_PRINTSRV, 0 }
+};
+
+static int lpr_init(const IniServiceAPI* const init_api)
+{
+ unsigned i;
+ for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++)
+ {
+ DebugFormat(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;
+ AppIdData* flowp = args->flowp;
+ const uint8_t* data = args->data;
+ const int dir = args->dir;
+ uint16_t size = args->size;
+
+ if (!size)
+ goto inprocess;
+
+ ld = (ServiceLPRData*)lpr_service_mod.api->data_get(flowp, lpr_service_mod.flow_data_index);
+ if (!ld)
+ {
+ ld = (ServiceLPRData*)snort_calloc(sizeof(ServiceLPRData));
+ lpr_service_mod.api->data_add(flowp, ld, lpr_service_mod.flow_data_index, &snort_free);
+ 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; i<size; i++)
+ if (!isprint(data[i]) || isspace(data[i]))
+ goto bail;
+ ld->state = 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; i++)
+ {
+ if (data[i] == 0x0A)
+ goto bail;
+ else if (isspace(data[i]))
+ break;
+ if (!isdigit(data[i]))
+ goto bail;
+ }
+ i++;
+ if (i >= size)
+ goto bail;
+ for (; i<size-1; i++)
+ if (!isprint(data[i]) || isspace(data[i]))
+ goto bail;
+ ld->state = 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);
+ return SERVICE_INPROCESS;
+
+success:
+ lpr_service_mod.api->add_service(flowp, args->pkt, dir, &svc_element,
+ APP_ID_PRINTSRV, nullptr, nullptr, nullptr);
+ return SERVICE_SUCCESS;
+
+fail:
+ lpr_service_mod.api->fail_service(flowp, args->pkt, dir, &svc_element,
+ lpr_service_mod.flow_data_index, args->pConfig);
+ return SERVICE_NOMATCH;
+
+bail:
+ lpr_service_mod.api->incompatible_data(flowp, args->pkt, dir, &svc_element,
+ lpr_service_mod.flow_data_index, args->pConfig);
+ return SERVICE_NOT_COMPATIBLE;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_lpr.h author Sourcefire Inc.
+
+#ifndef SERVICE_LPR_H
+#define SERVICE_LPR_H
+
+// Service detector for LPR
+
+#include "service_api.h"
+
+extern RNAServiceValidationModule lpr_service_mod;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_mdns.cc author Sourcefire Inc.
+
+#include "service_mdns.h"
+
+#include "search_engines/search_tool.h"
+#include "client_plugins/client_app_base.h"
+#include "detector_plugins/http_url_patterns.h"
+#include "detector_plugins/detector_http.h"
+#include "util/common_util.h"
+#include "appid_config.h"
+#include "appid_flow_data.h"
+#include "fw_appid.h"
+#include "http_common.h"
+#include "lua_detector_api.h"
+#include "service_api.h"
+#include "service_base.h"
+#include "service_ssl.h"
+
+#include "main/snort_debug.h"
+#include "utils/util.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
+
+enum MDNSState
+{
+ MDNS_STATE_CONNECTION,
+ MDNS_STATE_CONNECTION_ERROR
+};
+
+struct ServiceMDNSData
+{
+ MDNSState state;
+};
+
+struct MdnsPattern
+{
+ const uint8_t* pattern;
+ unsigned length;
+};
+
+struct MatchedPatterns
+{
+ MdnsPattern* mpattern;
+ int index;
+ MatchedPatterns* next;
+};
+
+struct MdnsConfig
+{
+ SearchTool* mdnsMatcher;
+ MatchedPatterns* patternList;
+};
+
+static int MDNS_init(const IniServiceAPI* 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 AppIdConfig* pConfig);
+static int MDNS_validate(ServiceValidationArgs* args);
+static int mdnsMatcherCreate(AppIdConfig* pConfig);
+static void mdnsMatcherDestroy(AppIdConfig* pConfig);
+static unsigned mdnsMatchListCreate(const char* data, uint16_t dataSize, const
+ AppIdConfig* pConfig);
+static void mdnsMatchListFind(const char* dataPtr, uint16_t index, const char** resp_endptr,
+ int* pattern_length, const AppIdConfig* pConfig);
+static void mdnsMatchListDestroy(const AppIdConfig* pConfig);
+static void MDNS_clean(const CleanServiceAPI* const clean_api);
+
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &MDNS_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "MDNS"
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &MDNS_validate, 5353, IpProtocol::UDP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+RNAServiceValidationModule mdns_service_mod =
+{
+ "MDNS",
+ &MDNS_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ MDNS_clean,
+ 0
+};
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_MDNS, APPINFO_FLAG_SERVICE_ADDITIONAL }
+};
+
+static int MDNS_init(const IniServiceAPI* const init_api)
+{
+ unsigned i;
+ for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++)
+ {
+ DebugFormat(DEBUG_INSPECTOR,"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 AppIdConfig* 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(AppIdData* flowp, const Packet* pkt, uint16_t size, const
+ AppIdConfig* pConfig)
+{
+ const char* query_val;
+ const char* answers;
+ char user_name[MAX_LENGTH_SERVICE_NAME] = "";
+ char* user_name_bkp = nullptr;
+ 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->data + QUERY_OFFSET;
+ query_val_int = (short)(query_val[0]<<SHIFT_BITS | query_val[1]);
+ answers = (char*)pkt->data + ANSWER_OFFSET;
+ ans_count = (short)(answers[0]<< SHIFT_BITS | (answers[1] ));
+
+ if ( query_val_int ==0)
+ {
+ srv_original = (char*)pkt->data + RECORD_OFFSET;
+ mdnsMatchListCreate(srv_original, size-RECORD_OFFSET, pConfig);
+ end_srv_original = (char*)pkt->data + 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;
+ AppIdData* flowp = args->flowp;
+ const uint8_t* data = args->data;
+ Packet* pkt = args->pkt;
+ uint16_t size = args->size;
+
+ fd = (ServiceMDNSData*)mdns_service_mod.api->data_get(flowp, mdns_service_mod.flow_data_index);
+ if (!fd)
+ {
+ fd = (ServiceMDNSData*)snort_calloc(sizeof(ServiceMDNSData));
+ mdns_service_mod.api->data_add(flowp, fd, mdns_service_mod.flow_data_index, &snort_free);
+ fd->state = MDNS_STATE_CONNECTION;
+ }
+
+ if (pkt->ptrs.dp == MDNS_PORT || pkt->ptrs.sp == MDNS_PORT )
+ {
+ ret_val = MDNS_validate_reply(data, size);
+ if (ret_val == 1)
+ {
+ if (pAppidActiveConfig->mod_config->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, nullptr, nullptr, nullptr);
+ return SERVICE_SUCCESS;
+
+fail:
+ mdns_service_mod.api->fail_service(flowp, pkt, args->dir, &svc_element,
+ mdns_service_mod.flow_data_index, args->pConfig);
+ return SERVICE_NOMATCH;
+}
+
+static MatchedPatterns* patternFreeList;
+
+static MdnsPattern 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(AppIdConfig* pConfig)
+{
+ MdnsConfig* pMdnsConfig = (MdnsConfig*)snort_calloc(sizeof(MdnsConfig));
+
+ if (!(pMdnsConfig->mdnsMatcher = new SearchTool("ac_full")))
+ {
+ snort_free(pMdnsConfig);
+ return 0;
+ }
+
+ for (unsigned i = 0; i < sizeof(patterns) / sizeof(*patterns); i++)
+ pMdnsConfig->mdnsMatcher->add(
+ (char*)patterns[i].pattern, patterns[i].length, &patterns[i]);
+
+ pMdnsConfig->mdnsMatcher->prep();
+ pConfig->add_generic_config_element(svc_element.name, pMdnsConfig);
+ return 1;
+}
+
+static void mdnsMatcherDestroy(AppIdConfig* pConfig)
+{
+ MdnsConfig* pMdnsConfig = (MdnsConfig*)pConfig->find_generic_config_element(svc_element.name);
+ MatchedPatterns* node;
+ if (pMdnsConfig->mdnsMatcher)
+ delete pMdnsConfig->mdnsMatcher;
+ pMdnsConfig->mdnsMatcher = nullptr;
+
+ mdnsMatchListDestroy(pConfig);
+
+ while ((node = patternFreeList))
+ {
+ patternFreeList = node->next;
+ snort_free(node);
+ }
+ snort_free(pMdnsConfig);
+ pConfig->remove_generic_config_element(svc_element.name);
+}
+
+static int mdns_pattern_match(void* id, void*, int index, void* data, void*)
+{
+ MatchedPatterns* cm;
+ MatchedPatterns** matches = (MatchedPatterns**)data;
+ MdnsPattern* target = (MdnsPattern*)id;
+ MatchedPatterns* element;
+ MatchedPatterns* prevElement;
+
+ if (patternFreeList)
+ {
+ cm = patternFreeList;
+ patternFreeList = cm->next;
+ }
+ else
+ cm = (MatchedPatterns*)snort_calloc(sizeof(MatchedPatterns));
+
+ cm->mpattern = target;
+ cm->index = index;
+ for (prevElement = nullptr, 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
+ AppIdConfig* pConfig)
+{
+ MdnsConfig* pMdnsConfig = (MdnsConfig*)((AppIdConfig*)pConfig)->find_generic_config_element(
+ svc_element.name);
+
+ if (pMdnsConfig->patternList)
+ mdnsMatchListDestroy(pConfig);
+
+ pMdnsConfig->mdnsMatcher->find_all(
+ (char*)data, dataSize, mdns_pattern_match, false, (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 AppIdConfig* pConfig)
+{
+ MdnsConfig* pMdnsConfig = (MdnsConfig*)((AppIdConfig*)pConfig)->find_generic_config_element(
+ 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 = nullptr;
+ *pattern_length = 0;
+}
+
+static void mdnsMatchListDestroy(const AppIdConfig* pConfig)
+{
+ MatchedPatterns* element;
+
+ MdnsConfig* pMdnsConfig = (MdnsConfig*)((AppIdConfig*)pConfig)->find_generic_config_element(
+ 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);
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_mdns.h author Sourcefire Inc.
+
+#ifndef SERVICE_MDNS_H
+#define SERVICE_MDNS_H
+
+#include "service_api.h"
+
+extern RNAServiceValidationModule mdns_service_mod;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_mysql.cc author Sourcefire Inc.
+
+#include "service_mysql.h"
+
+#include "main/snort_debug.h"
+
+#include "application_ids.h"
+#include "app_info_table.h"
+#include "appid_flow_data.h"
+
+#include "service_api.h"
+
+#pragma pack(1)
+
+struct ServiceMYSQLHdr
+{
+ union
+ {
+ uint32_t len;
+ struct
+ {
+ uint8_t len[3];
+ uint8_t packet;
+ } p;
+ } l;
+ IpProtocol proto;
+};
+
+#pragma pack()
+
+static int svc_mysql_init(const IniServiceAPI* const init_api);
+static int svc_mysql_validate(ServiceValidationArgs* args);
+
+static RNAServiceElement svc_element
+{
+ nullptr,
+ &svc_mysql_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "mysql"
+};
+
+static RNAServiceValidationPort pp[]
+{
+ { &svc_mysql_validate, 3306, IpProtocol::TCP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+RNAServiceValidationModule mysql_service_mod
+{
+ "MYSQL",
+ &svc_mysql_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static AppRegistryEntry appIdRegistry[]
+{
+ { APP_ID_MYSQL, APPINFO_FLAG_SERVICE_ADDITIONAL }
+};
+
+static int svc_mysql_init(const IniServiceAPI* const init_api)
+{
+ for ( unsigned i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++ )
+ {
+ DebugFormat(DEBUG_INSPECTOR,"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 = nullptr;
+ AppIdData* 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 != (IpProtocol)0x0A)
+ goto fail;
+
+ end = data + len;
+ data += sizeof(ServiceMYSQLHdr);
+ p = data;
+ for (; data<end && *data; data++)
+ {
+ if (!isprint(*data))
+ goto fail;
+ }
+ if (data >= end)
+ goto fail;
+ if (data == p)
+ p = nullptr;
+ data += 5;
+ if (data >= end)
+ goto fail;
+ for (; data<end && *data; data++)
+ {
+ if (!isprint(*data))
+ goto fail;
+ }
+ data += 6;
+ if (data >= end)
+ goto fail;
+ mysql_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element,
+ APP_ID_MYSQL, nullptr, (char*)p, nullptr);
+ return SERVICE_SUCCESS;
+
+inprocess:
+ mysql_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &svc_element);
+ 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);
+ return SERVICE_NOMATCH;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_mysql.h author Sourcefire Inc.
+
+#ifndef SERVICE_MYSQL_H
+#define SERVICE_MYSQL_H
+
+#include "service_api.h"
+
+extern RNAServiceValidationModule mysql_service_mod;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_netbios.cc author Sourcefire Inc.
+
+#include "service_netbios.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h" /* for WORDS_BIGENDIAN */
+#endif
+
+#include "log/messages.h"
+#include "main/snort_debug.h"
+#include "utils/util.h"
+
+#include "app_info_table.h"
+#include "appid_api.h"
+#include "appid_flow_data.h"
+#include "application_ids.h"
+#include "dcerpc.h"
+#include "service_api.h"
+
+/*#define RNA_DEBUG_NETBIOS 1 */
+
+#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 4
+
+#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
+
+enum NBSSState
+{
+ NBSS_STATE_CONNECTION,
+ NBSS_STATE_FLOW,
+ NBSS_STATE_CONT,
+ NBSS_STATE_ERROR
+};
+
+#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)
+
+struct NBNSHeader
+{
+ 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;
+};
+
+#define NBNS_NAME_LEN 0x20
+struct NBNSLabelLength
+{
+ uint8_t len;
+};
+
+struct NBNSLabelData
+{
+ uint8_t len;
+ uint8_t data[NBNS_NAME_LEN];
+ uint8_t zero;
+};
+
+struct NBNSLabel
+{
+ uint16_t type;
+ uint16_t class_id;
+};
+
+struct NBNSLabelPtr
+{
+ uint8_t flag;
+ uint8_t position;
+};
+
+struct NBNSAnswerData
+{
+ uint32_t ttl;
+ uint16_t data_len;
+};
+
+struct NBSSHeader
+{
+ uint8_t type;
+ uint8_t flags;
+ uint16_t length;
+};
+
+uint8_t NB_SMB_BANNER[] =
+{
+ 0xFF, 'S', 'M', 'B'
+};
+
+struct ServiceSMBHeader
+{
+ 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;
+};
+
+struct ServiceSMBAndXResponse
+{
+ uint8_t wc;
+ uint8_t cmd;
+ uint8_t reserved;
+ uint16_t offset;
+ uint16_t action;
+ uint16_t sec_len;
+};
+
+struct ServiceSMBNegotiateProtocolResponse
+{
+ 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;
+};
+
+struct ServiceSMBTransactionHeader
+{
+ 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;
+};
+/* 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";
+
+struct ServiceSMBBrowserHeader
+{
+ uint8_t command;
+ uint8_t count;
+ uint32_t period;
+ uint8_t hostname[16];
+ uint8_t major;
+ uint8_t minor;
+ uint32_t server_type;
+};
+
+struct ServiceNBSSData
+{
+ NBSSState state;
+ unsigned count;
+ uint32_t length;
+ AppId serviceAppId;
+ AppId miscAppId;
+};
+
+struct NBDgmHeader
+{
+ 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;
+};
+
+struct NBDgmError
+{
+ uint8_t code;
+};
+
+#pragma pack()
+
+static int netbios_init(const IniServiceAPI* const init_api);
+static int nbns_validate(ServiceValidationArgs* args);
+static int nbss_validate(ServiceValidationArgs* args);
+static int nbdgm_validate(ServiceValidationArgs* args);
+
+static RNAServiceElement nbns_svc_element
+{
+ nullptr,
+ &nbns_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "nbns"
+};
+static RNAServiceElement nbdgm_svc_element
+{
+ nullptr,
+ &nbdgm_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "nbdgm"
+};
+static RNAServiceElement nbss_svc_element
+{
+ nullptr,
+ &nbss_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "nbss"
+};
+
+static RNAServiceValidationPort pp[]
+{
+ { &nbns_validate, 137, IpProtocol::TCP, 0 },
+ { &nbns_validate, 137, IpProtocol::UDP, 0 },
+ { &nbns_validate, 137, IpProtocol::UDP, 1 },
+ { &nbdgm_validate, 138, IpProtocol::UDP, 0 },
+ { &nbss_validate, 139, IpProtocol::TCP, 0 },
+ { &nbss_validate, 445, IpProtocol::TCP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+RNAServiceValidationModule netbios_service_mod
+{
+ "NETBIOS",
+ &netbios_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static int netbios_init(const IniServiceAPI* const init_api)
+{
+ init_api->RegisterPattern(&nbss_validate, IpProtocol::TCP, NB_SMB_BANNER,
+ sizeof(NB_SMB_BANNER),
+ -1, "netbios", init_api->pAppidConfig);
+
+ DebugFormat(DEBUG_INSPECTOR,"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);
+
+ DebugFormat(DEBUG_INSPECTOR,"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);
+
+ DebugFormat(DEBUG_INSPECTOR,"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);
+
+ DebugFormat(DEBUG_INSPECTOR,"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; i<NBNS_NAME_LEN; i++)
+ if (lbl_data->data[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;
+ AppIdData* 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; i<count; i++)
+ {
+ if (nbns_validate_query(&data, begin, end))
+ goto fail;
+ }
+ }
+
+ if (hdr->ACount)
+ {
+ count = ntohs(hdr->ACount);
+ for (i=0; i<count; i++)
+ {
+ if (nbns_validate_answer(&data, begin, end))
+ goto fail;
+ }
+ }
+
+ if (hdr->NSCount)
+ {
+ count = ntohs(hdr->NSCount);
+ for (i=0; i<count; i++)
+ {
+ if (nbns_validate_answer(&data, begin, end))
+ goto fail;
+ }
+ }
+
+ if (hdr->ARCount)
+ {
+ count = ntohs(hdr->ARCount);
+ for (i=0; i<count; i++)
+ {
+ if (nbns_validate_answer(&data, begin, end))
+ goto fail;
+ }
+ }
+
+ if (dir == APP_ID_FROM_INITIATOR)
+ {
+ setAppIdFlag(flowp, APPID_SESSION_UDP_REVERSED);
+ goto inprocess;
+ }
+
+success:
+ netbios_service_mod.api->add_service(flowp, args->pkt, dir, &nbns_svc_element,
+ APP_ID_NETBIOS_NS, nullptr, nullptr, nullptr);
+ return SERVICE_SUCCESS;
+
+inprocess:
+ netbios_service_mod.api->service_inprocess(flowp, args->pkt, dir, &nbns_svc_element);
+ 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);
+ 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);
+ return SERVICE_NOT_COMPATIBLE;
+}
+
+static void nbss_free_state(void* data)
+{
+ ServiceNBSSData* nd = (ServiceNBSSData*)data;
+
+ if (nd)
+ {
+ snort_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,
+ AppIdData* flowp, const Packet* 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->ptrs.sp,
+ pkt->dst_ip.s_addr, pkt->ptrs.dp);
+#else
+ UNUSED(pkt);
+#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 = snort_strdup(domain);
+ }
+}
+
+static int nbss_validate(ServiceValidationArgs* args)
+{
+ ServiceNBSSData* nd;
+ const NBSSHeader* hdr;
+ const uint8_t* end;
+ uint32_t tmp;
+ int retval = -1;
+ AppIdData* flowp = args->flowp;
+ Packet* 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 = (ServiceNBSSData*)netbios_service_mod.api->data_get(flowp,
+ netbios_service_mod.flow_data_index);
+ if (!nd)
+ {
+ nd = (ServiceNBSSData*)snort_calloc(sizeof(ServiceNBSSData));
+ netbios_service_mod.api->data_add(flowp, nd,
+ netbios_service_mod.flow_data_index, &nbss_free_state);
+ 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, nullptr, nullptr, nullptr) == 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);
+ }
+ 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);
+ }
+ 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;
+ AppId serviceAppId = APP_ID_NETBIOS_DGM;
+ AppId miscAppId = APP_ID_NONE;
+ AppIdData* flowp = args->flowp;
+ const uint8_t* data = args->data;
+ Packet* 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->ptrs.sp != pkt->ptrs.dp)
+ 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);
+ }
+ 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, nullptr, nullptr, nullptr) == 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);
+ }
+ return SERVICE_INPROCESS;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_netbios.h author Sourcefire Inc.
+
+#ifndef SERVICE_NETBIOS_H
+#define SERVICE_NETBIOS_H
+
+#include "service_api.h"
+
+extern RNAServiceValidationModule netbios_service_mod;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_nntp.cc author Sourcefire Inc.
+
+#include "service_nntp.h"
+
+#include "main/snort_debug.h"
+#include "utils/util.h"
+
+#include "appid_flow_data.h"
+#include "application_ids.h"
+#include "service_api.h"
+
+#define NNTP_PORT 119
+
+#define NNTP_COUNT_THRESHOLD 4
+
+enum NNTPState
+{
+ NNTP_STATE_CONNECTION,
+ NNTP_STATE_TRANSFER,
+ NNTP_STATE_DATA,
+ NNTP_STATE_CONNECTION_ERROR
+};
+
+#define NNTP_CR_RECEIVED 0x0001
+#define NNTP_MID_LINE 0x0002
+#define NNTP_MID_TERM 0x0004
+
+struct ServiceNNTPData
+{
+ NNTPState state;
+ uint32_t flags;
+ unsigned count;
+};
+
+#pragma pack(1)
+
+struct ServiceNNTPCode
+{
+ uint8_t code[3];
+ uint8_t sp;
+};
+
+#pragma pack()
+
+static int nntp_init(const IniServiceAPI* const init_api);
+static int nntp_validate(ServiceValidationArgs* args);
+
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &nntp_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "nntp"
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &nntp_validate, NNTP_PORT, IpProtocol::TCP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+RNAServiceValidationModule nntp_service_mod =
+{
+ "NNTP",
+ &nntp_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+#define NNTP_PATTERN1 "200 "
+#define NNTP_PATTERN2 "201 "
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_NNTP, 0 }
+};
+
+static int nntp_init(const IniServiceAPI* const init_api)
+{
+ init_api->RegisterPattern(&nntp_validate, IpProtocol::TCP, (uint8_t*)NNTP_PATTERN1,
+ sizeof(NNTP_PATTERN1)-1, 0, "nntp", init_api->pAppidConfig);
+ init_api->RegisterPattern(&nntp_validate, IpProtocol::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++)
+ {
+ DebugFormat(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 (; *offset<size; (*offset)++)
+ {
+ if (data[*offset] != 0x0D && data[*offset] != 0x0A)
+ break;
+ }
+
+ if (size - *offset < (int)sizeof(ServiceNNTPCode))
+ {
+ for (; *offset<size; (*offset)++)
+ {
+ if (!isspace(data[*offset]))
+ return -1;
+ }
+ return 0;
+ }
+
+ code_hdr = (ServiceNNTPCode*)(data + *offset);
+
+ if (code_hdr->sp != ' ')
+ 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;
+ AppIdData* 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 = (ServiceNNTPData*)nntp_service_mod.api->data_get(flowp, nntp_service_mod.flow_data_index);
+ if (!nd)
+ {
+ nd = (ServiceNNTPData*)snort_calloc(sizeof(ServiceNNTPData));
+ nntp_service_mod.api->data_add(flowp, nd, nntp_service_mod.flow_data_index, &snort_free);
+ 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);
+ return SERVICE_INPROCESS;
+
+success:
+ nntp_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element,
+ APP_ID_NNTP, nullptr, nullptr, nullptr);
+ 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);
+ return SERVICE_NOMATCH;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_nntp.h author Sourcefire Inc.
+
+#ifndef SERVICE_NNTP_H
+#define SERVICE_NNTP_H
+
+#include "service_api.h"
+
+extern RNAServiceValidationModule nntp_service_mod;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_ntp.cc author Sourcefire Inc.
+
+#include "service_ntp.h"
+
+#include "main/snort_debug.h"
+
+#include "appid_flow_data.h"
+#include "application_ids.h"
+#include "service_api.h"
+
+#pragma pack(1)
+
+struct ServiceNTPTimestamp
+{
+ uint32_t sec;
+ uint32_t frac;
+};
+
+struct ServiceNTPHeader
+{
+ 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;
+};
+
+struct ServiceNTPOptional
+{
+ uint32_t keyid;
+ uint32_t digest[4];
+};
+
+#pragma pack()
+
+static int ntp_init(const IniServiceAPI* const init_api);
+static int ntp_validate(ServiceValidationArgs* args);
+
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &ntp_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "ntp"
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &ntp_validate, 123, IpProtocol::UDP, 0 },
+ { &ntp_validate, 123, IpProtocol::TCP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+RNAServiceValidationModule ntp_service_mod =
+{
+ "NTP",
+ &ntp_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_NTP, 0 }
+};
+
+static int ntp_init(const IniServiceAPI* const init_api)
+{
+ unsigned i;
+ for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++)
+ {
+ DebugFormat(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;
+ AppIdData* 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, nullptr, nullptr, nullptr);
+ return SERVICE_SUCCESS;
+
+inprocess:
+ ntp_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &svc_element);
+ 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);
+ return SERVICE_NOMATCH;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_ntp.h author Sourcefire Inc.
+
+#ifndef SERVICE_NTP_H
+#define SERVICE_NTP_H
+
+#include "service_api.h"
+
+extern RNAServiceValidationModule ntp_service_mod;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_radius.cc author Sourcefire Inc.
+
+#include "service_radius.h"
+
+#include "main/snort_debug.h"
+#include "utils/util.h"
+
+#include "app_info_table.h"
+#include "appid_flow_data.h"
+#include "application_ids.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
+
+enum RADIUSState
+{
+ RADIUS_STATE_REQUEST,
+ RADIUS_STATE_RESPONSE
+};
+
+struct ServiceRADIUSData
+{
+ RADIUSState state;
+ uint8_t id;
+};
+
+#pragma pack(1)
+
+struct RADIUSHeader
+{
+ uint8_t code;
+ uint8_t id;
+ uint16_t length;
+ uint8_t auth[16];
+};
+
+#pragma pack()
+
+static int radius_init(const IniServiceAPI* const init_api);
+static int radius_validate(ServiceValidationArgs* args);
+static int radius_validate_accounting(ServiceValidationArgs* args);
+
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &radius_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "radius"
+};
+
+static RNAServiceElement acct_svc_element =
+{
+ nullptr,
+ &radius_validate_accounting,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "radacct"
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &radius_validate, 1812, IpProtocol::UDP, 0 },
+ { &radius_validate, 1812, IpProtocol::UDP, 1 },
+ { &radius_validate_accounting, 1813, IpProtocol::UDP, 0 },
+ { &radius_validate_accounting, 1813, IpProtocol::UDP, 1 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+RNAServiceValidationModule radius_service_mod =
+{
+ "RADIUS",
+ &radius_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_RADIUS_ACCT, APPINFO_FLAG_SERVICE_UDP_REVERSED },
+ { APP_ID_RADIUS, APPINFO_FLAG_SERVICE_UDP_REVERSED }
+};
+
+static int radius_init(const IniServiceAPI* const init_api)
+{
+ unsigned i;
+ for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++)
+ {
+ DebugFormat(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;
+ AppIdData* flowp = args->flowp;
+ const int dir = args->dir;
+ uint16_t size = args->size;
+
+ if (!size)
+ goto inprocess;
+ if (size < sizeof(RADIUSHeader))
+ goto fail;
+
+ rd = (ServiceRADIUSData*)radius_service_mod.api->data_get(flowp,
+ radius_service_mod.flow_data_index);
+ if (!rd)
+ {
+ rd = (ServiceRADIUSData*)snort_calloc(sizeof(ServiceRADIUSData));
+ radius_service_mod.api->data_add(flowp, rd, radius_service_mod.flow_data_index,
+ &snort_free);
+ 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);
+ return SERVICE_INPROCESS;
+
+success:
+ radius_service_mod.api->add_service(flowp, args->pkt, dir, &svc_element,
+ APP_ID_RADIUS, nullptr, nullptr, nullptr);
+ 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);
+ 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);
+ 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;
+ AppIdData* flowp = args->flowp;
+ const int dir = args->dir;
+ uint16_t size = args->size;
+
+ if (!size)
+ goto inprocess;
+ if (size < sizeof(RADIUSHeader))
+ goto fail;
+
+ rd = (ServiceRADIUSData*)radius_service_mod.api->data_get(flowp,
+ radius_service_mod.flow_data_index);
+ if (!rd)
+ {
+ rd = (ServiceRADIUSData*)snort_calloc(sizeof(ServiceRADIUSData));
+ radius_service_mod.api->data_add(flowp, rd, radius_service_mod.flow_data_index,
+ &snort_free);
+ 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);
+ return SERVICE_INPROCESS;
+
+success:
+ radius_service_mod.api->add_service(flowp, args->pkt, dir, &acct_svc_element,
+ APP_ID_RADIUS_ACCT, nullptr, nullptr, nullptr);
+ 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);
+ 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);
+ return SERVICE_NOMATCH;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_radius.h author Sourcefire Inc.
+
+#ifndef SERVICE_RADIUS_H
+#define SERVICE_RADIUS_H
+
+#include "service_api.h"
+
+extern RNAServiceValidationModule radius_service_mod;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_rexec.cc author Sourcefire Inc.
+
+#include "service_rexec.h"
+
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include "protocols/packet.h"
+#include "main/snort_debug.h"
+#include "target_based/snort_protocols.h"
+#include "utils/util.h"
+
+#include "appid_api.h"
+#include "app_info_table.h"
+#include "appid_flow_data.h"
+#include "application_ids.h"
+#include "service_api.h"
+#include "service_base.h"
+
+#define REXEC_PORT 512
+#define REXEC_MAX_PORT_PACKET 6
+
+enum REXECState
+{
+ REXEC_STATE_PORT,
+ REXEC_STATE_SERVER_CONNECT,
+ REXEC_STATE_USERNAME,
+ REXEC_STATE_PASSWORD,
+ REXEC_STATE_COMMAND,
+ REXEC_STATE_REPLY,
+ REXEC_STATE_DONE,
+ REXEC_STATE_STDERR_CONNECT_SYN,
+ REXEC_STATE_STDERR_CONNECT_SYN_ACK
+};
+
+struct ServiceREXECData
+{
+ REXECState state;
+ struct ServiceREXECData* parent;
+ struct ServiceREXECData* child;
+};
+
+static int rexec_init(const IniServiceAPI* const init_api);
+static int rexec_validate(ServiceValidationArgs* args);
+
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &rexec_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "rexec"
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &rexec_validate, REXEC_PORT, IpProtocol::TCP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+RNAServiceValidationModule rexec_service_mod =
+{
+ "REXEC",
+ &rexec_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_EXEC, APPINFO_FLAG_SERVICE_ADDITIONAL }
+};
+
+static int16_t app_id = 0;
+
+static int rexec_init(const IniServiceAPI* const init_api)
+{
+ unsigned i;
+
+ app_id = AddProtocolReference("rexec");
+
+ for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++)
+ {
+ DebugFormat(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId);
+ init_api->RegisterAppId(&rexec_validate, appIdRegistry[i].appId,
+ appIdRegistry[i].additionalInfo, init_api->pAppidConfig);
+ }
+
+ return 0;
+}
+
+static void rexec_free_state(void* data)
+{
+ ServiceREXECData* rd = (ServiceREXECData*)data;
+
+ if (rd)
+ {
+ if (rd->parent)
+ {
+ rd->parent->child = nullptr;
+ rd->parent->parent = nullptr;
+ }
+ if (rd->child)
+ {
+ rd->child->parent = nullptr;
+ rd->child->child = nullptr;
+ }
+ snort_free(rd);
+ }
+}
+
+static int rexec_validate(ServiceValidationArgs* args)
+{
+ int i;
+ uint32_t port;
+ AppIdData* pf;
+ AppIdData* flowp = args->flowp;
+ const uint8_t* data = args->data;
+ Packet* pkt = args->pkt;
+ const int dir = args->dir;
+ uint16_t size = args->size;
+
+ ServiceREXECData* rd = (ServiceREXECData*)rexec_service_mod.api->data_get(
+ flowp, rexec_service_mod.flow_data_index);
+
+ if (!rd)
+ {
+ if (!size)
+ goto inprocess;
+ rd = (ServiceREXECData*)snort_calloc(sizeof(ServiceREXECData));
+ rexec_service_mod.api->data_add(flowp, rd,
+ rexec_service_mod.flow_data_index, &rexec_free_state);
+ rd->state = REXEC_STATE_PORT;
+ }
+
+ 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<size-1; i++)
+ {
+ if (!isdigit(data[i]))
+ goto bail;
+ port *= 10;
+ port += data[i] - '0';
+ }
+ if (port > 65535)
+ goto bail;
+ if (port && pkt)
+ {
+ const sfip_t* sip;
+ const sfip_t* dip;
+
+ dip = pkt->ptrs.ip_api.get_dst();
+ sip = pkt->ptrs.ip_api.get_src();
+ pf = rexec_service_mod.api->flow_new(flowp, pkt, dip, 0, sip, (uint16_t)port,
+ IpProtocol::TCP, app_id,
+ APPID_EARLY_SESSION_FLAG_FW_RULE);
+ if (pf)
+ {
+ ServiceREXECData* tmp_rd = (ServiceREXECData*)snort_calloc(
+ sizeof(ServiceREXECData));
+ tmp_rd->state = REXEC_STATE_STDERR_CONNECT_SYN;
+ tmp_rd->parent = rd;
+
+ rexec_service_mod.api->data_add(pf, tmp_rd,
+ rexec_service_mod.flow_data_index, &rexec_free_state);
+ 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 = nullptr;
+ return SERVICE_ENULL;
+ }
+ rd->child = tmp_rd;
+ rd->state = REXEC_STATE_SERVER_CONNECT;
+ 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_SERVICE_DETECTED |
+ APPID_SESSION_NOT_A_SERVICE |
+ APPID_SESSION_PORT_SERVICE_DONE);
+ pf->rnaServiceState = RNA_STATE_STATEFUL;
+ }
+ 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; i<size && data[i]; i++)
+ if (!isprint(data[i]) || isspace(data[i]))
+ goto bail;
+ rd->state = 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; i<size && data[i]; i++)
+ if (!isprint(data[i]))
+ goto bail;
+ rd->state = 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; i<size && data[i]; i++)
+ if (!isprint(data[i]))
+ goto bail;
+ rd->state = 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; i<size && data[i]; i++)
+ {
+ if (!isprint(data[i]))
+ goto bail;
+ }
+ i++;
+ if (i != size)
+ goto bail;
+ rd->state = 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;
+ goto success;
+ break;
+ 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;
+ clearAppIdFlag(flowp, APPID_SESSION_REXEC_STDERR);
+ }
+ goto bail;
+ default:
+ goto bail;
+ }
+
+inprocess:
+ if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED))
+ {
+ rexec_service_mod.api->service_inprocess(flowp, pkt, dir, &svc_element);
+ }
+ return SERVICE_INPROCESS;
+
+success:
+ if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED))
+ {
+ rexec_service_mod.api->add_service(flowp, pkt, dir, &svc_element,
+ APP_ID_EXEC, nullptr, nullptr, nullptr);
+ }
+ return SERVICE_SUCCESS;
+
+bail:
+ if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED))
+ {
+ rexec_service_mod.api->incompatible_data(flowp, pkt, dir, &svc_element,
+ rexec_service_mod.flow_data_index,
+ args->pConfig);
+ }
+ clearAppIdFlag(flowp, APPID_SESSION_CONTINUE);
+ return SERVICE_NOT_COMPATIBLE;
+
+fail:
+ if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED))
+ {
+ rexec_service_mod.api->fail_service(flowp, pkt, dir, &svc_element,
+ rexec_service_mod.flow_data_index,
+ args->pConfig);
+ }
+ clearAppIdFlag(flowp, APPID_SESSION_CONTINUE);
+ return SERVICE_NOMATCH;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_rexec.h author Sourcefire Inc.
+
+#ifndef SERVICE_REXEC_H
+#define SERVICE_REXEC_H
+
+#include "service_api.h"
+
+extern RNAServiceValidationModule rexec_service_mod;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_rfb.cc author Sourcefire Inc.
+
+#include "service_rfb.h"
+
+#include "service_api.h"
+#include "app_info_table.h"
+#include "application_ids.h"
+
+#include "main/snort_debug.h"
+
+#define RFB_BANNER_SIZE 12
+
+#define RFB_BANNER "RFB "
+
+static int rfb_init(const IniServiceAPI* const init_api);
+static int rfb_validate(ServiceValidationArgs* args);
+
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &rfb_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "rfb"
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &rfb_validate, 5900, IpProtocol::TCP, 0 },
+ { &rfb_validate, 5901, IpProtocol::TCP, 0 },
+ { &rfb_validate, 5902, IpProtocol::TCP, 0 },
+ { &rfb_validate, 5903, IpProtocol::TCP, 0 },
+ { &rfb_validate, 5904, IpProtocol::TCP, 0 },
+ { &rfb_validate, 5905, IpProtocol::TCP, 0 },
+ { &rfb_validate, 5906, IpProtocol::TCP, 0 },
+ { &rfb_validate, 5907, IpProtocol::TCP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+RNAServiceValidationModule rfb_service_mod =
+{
+ "RFB",
+ &rfb_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_VNC, APPINFO_FLAG_SERVICE_ADDITIONAL },
+ { APP_ID_VNC_RFB, APPINFO_FLAG_SERVICE_ADDITIONAL }
+};
+
+static int rfb_init(const IniServiceAPI* const init_api)
+{
+ init_api->RegisterPattern(&rfb_validate, IpProtocol::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++)
+ {
+ DebugFormat(DEBUG_INSPECTOR,"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;
+ AppIdData* 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; i<RFB_BANNER_SIZE-1; i++)
+ {
+ *v = *p;
+ v++;
+ p++;
+ }
+ *v = 0;
+ rfb_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element,
+ APP_ID_VNC_RFB, nullptr, version, nullptr);
+ return SERVICE_SUCCESS;
+
+inprocess:
+ rfb_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &svc_element);
+ 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);
+ return SERVICE_NOMATCH;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_rfb.h author Sourcefire Inc.
+
+#ifndef SERVICE_RFB_H
+#define SERVICE_RFB_H
+
+struct RNAServiceValidationModule;
+extern RNAServiceValidationModule rfb_service_mod;
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_rlogin.cc author Sourcefire Inc.
+
+#include "service_rlogin.h"
+
+#include "service_api.h"
+#include "application_ids.h"
+
+#include "main/snort_debug.h"
+#include "protocols/tcp.h"
+#include "utils/util.h"
+
+#define RLOGIN_PASSWORD "Password: "
+enum RLOGINState
+{
+ RLOGIN_STATE_HANDSHAKE,
+ RLOGIN_STATE_PASSWORD,
+ RLOGIN_STATE_CRLF,
+ RLOGIN_STATE_DATA,
+ RLOGIN_STATE_DONE
+};
+
+struct ServiceRLOGINData
+{
+ RLOGINState state;
+};
+
+static int rlogin_init(const IniServiceAPI* const init_api);
+static int rlogin_validate(ServiceValidationArgs* args);
+
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &rlogin_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "rlogin"
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &rlogin_validate, 513, IpProtocol::TCP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+RNAServiceValidationModule rlogin_service_mod =
+{
+ "RLOGIN",
+ &rlogin_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_RLOGIN, 0 }
+};
+
+static int rlogin_init(const IniServiceAPI* const init_api)
+{
+ unsigned i;
+ for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++)
+ {
+ DebugFormat(DEBUG_INSPECTOR,"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;
+ AppIdData* flowp = args->flowp;
+ Packet* 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 = (ServiceRLOGINData*)rlogin_service_mod.api->data_get(flowp,
+ rlogin_service_mod.flow_data_index);
+ if (!rd)
+ {
+ rd = (ServiceRLOGINData*)snort_calloc(sizeof(ServiceRLOGINData));
+ rlogin_service_mod.api->data_add(flowp, rd, rlogin_service_mod.flow_data_index,
+ &snort_free);
+ 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->ptrs.tcph->are_flags_set(TH_URG) && size >= pkt->ptrs.tcph->urp())
+ {
+ 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);
+ return SERVICE_INPROCESS;
+
+success:
+ rlogin_service_mod.api->add_service(flowp, pkt, args->dir, &svc_element,
+ APP_ID_RLOGIN, nullptr, nullptr, nullptr);
+ return SERVICE_SUCCESS;
+
+fail:
+ rlogin_service_mod.api->fail_service(flowp, pkt, args->dir, &svc_element,
+ rlogin_service_mod.flow_data_index, args->pConfig);
+ return SERVICE_NOMATCH;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_rlogin.h author Sourcefire Inc.
+
+#ifndef SERVICE_RLOGIN_H
+#define SERVICE_RLOGIN_H
+
+struct RNAServiceValidationModule;
+extern RNAServiceValidationModule rlogin_service_mod;
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_rpc.cc author Sourcefire Inc.
+
+#include "service_rpc.h"
+
+#include <netdb.h>
+
+#if defined(FREEBSD) || defined(OPENBSD)
+#include "rpc/rpc.h"
+#endif
+
+#include "service_api.h"
+#include "app_info_table.h"
+
+#include "log/messages.h"
+#include "main/snort_debug.h"
+#include "application_ids.h"
+#include "target_based/snort_protocols.h"
+#include "utils/util.h"
+
+/*#define RNA_DEBUG_RPC 1 */
+
+enum RPCState
+{
+ RPC_STATE_CALL,
+ RPC_STATE_REPLY,
+ RPC_STATE_DONE
+};
+
+enum RPCTCPState
+{
+ 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
+};
+
+enum RPCReplyState
+{
+ RPC_REPLY_BEGIN,
+ RPC_REPLY_MULTI,
+ RPC_REPLY_MID
+};
+
+#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)
+
+struct ServiceRPCFragment
+{
+ uint32_t length;
+};
+
+struct ServiceRPCAuth
+{
+ uint32_t flavor;
+ uint32_t length;
+};
+
+struct ServiceRPCPortmap
+{
+ uint32_t program;
+ uint32_t version;
+ uint32_t proto;
+ uint32_t port;
+};
+
+struct ServiceRPCPortmapReply
+{
+ uint32_t port;
+};
+
+struct ServiceRPC
+{
+ uint32_t xid;
+ uint32_t type;
+};
+
+struct ServiceRPCCall
+{
+ ServiceRPC header;
+ uint32_t version;
+ uint32_t program;
+ uint32_t program_version;
+ uint32_t procedure;
+ ServiceRPCAuth cred;
+ ServiceRPCAuth verify;
+};
+
+struct ServiceRPCReply
+{
+ ServiceRPC header;
+ uint32_t reply_state;
+ ServiceRPCAuth verify;
+ uint32_t state;
+};
+
+#pragma pack()
+
+struct ServiceRPCData
+{
+ 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;
+};
+
+static int rpc_init(const IniServiceAPI* const init_api);
+static int rpc_validate(ServiceValidationArgs* args);
+static int rpc_tcp_validate(ServiceValidationArgs* args);
+
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &rpc_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "rpc"
+};
+static RNAServiceElement tcp_svc_element =
+{
+ nullptr,
+ &rpc_tcp_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "tcp rpc"
+};
+
+#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, IpProtocol::UDP, 0 },
+ { &rpc_validate, RPC_PORT_PORTMAPPER, IpProtocol::UDP, 1 },
+ { &rpc_tcp_validate, RPC_PORT_PORTMAPPER, IpProtocol::TCP, 0 },
+ { &rpc_validate, RPC_PORT_NFS, IpProtocol::UDP, 0 },
+ { &rpc_validate, RPC_PORT_NFS, IpProtocol::UDP, 1 },
+ { &rpc_tcp_validate, RPC_PORT_NFS, IpProtocol::TCP, 0 },
+ { &rpc_validate, RPC_PORT_MOUNTD, IpProtocol::UDP, 0 },
+ { &rpc_validate, RPC_PORT_MOUNTD, IpProtocol::UDP, 1 },
+ { &rpc_tcp_validate, RPC_PORT_MOUNTD, IpProtocol::TCP, 0 },
+ { &rpc_validate, RPC_PORT_NLOCKMGR, IpProtocol::UDP, 0 },
+ { &rpc_validate, RPC_PORT_NLOCKMGR, IpProtocol::UDP, 1 },
+ { &rpc_tcp_validate, RPC_PORT_NLOCKMGR, IpProtocol::TCP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+RNAServiceValidationModule rpc_service_mod =
+{
+ "RPC",
+ &rpc_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+struct RPCProgram
+{
+ RPCProgram* next;
+ uint32_t program;
+ char* name;
+};
+
+static RPCProgram* rpc_programs;
+
+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 AppRegistryEntry 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 IniServiceAPI* const init_api)
+{
+ struct rpcent* rpc;
+ RPCProgram* prog;
+
+ app_id = AddProtocolReference("sunrpc");
+
+ if (!rpc_programs)
+ {
+ while ((rpc = getrpcent()))
+ {
+ if (rpc->r_name)
+ {
+ prog = (RPCProgram*)snort_calloc(sizeof(RPCProgram));
+ prog->program = rpc->r_number;
+ prog->next = rpc_programs;
+ rpc_programs = prog;
+ prog->name = snort_strdup(rpc->r_name);
+ }
+ }
+ endrpcent();
+ }
+
+ init_api->RegisterPattern(&rpc_tcp_validate, IpProtocol::TCP, rpc_reply_accepted_pattern,
+ sizeof(rpc_reply_accepted_pattern), 8, "rpc", init_api->pAppidConfig);
+ init_api->RegisterPattern(&rpc_tcp_validate, IpProtocol::TCP, rpc_reply_denied_pattern,
+ sizeof(rpc_reply_denied_pattern), 8, "rpc", init_api->pAppidConfig);
+ init_api->RegisterPattern(&rpc_validate, IpProtocol::UDP, rpc_reply_accepted_pattern,
+ sizeof(rpc_reply_accepted_pattern), 4, "rpc", init_api->pAppidConfig);
+ init_api->RegisterPattern(&rpc_validate, IpProtocol::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++)
+ {
+ DebugFormat(DEBUG_INSPECTOR,"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,
+ AppIdData* flowp, Packet* 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;
+ AppIdData* pf;
+ const RPCProgram* rprog;
+
+ if (!size)
+ return SERVICE_INPROCESS;
+
+ end = data + size;
+
+ if (flowp->proto == IpProtocol::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)
+ {
+ const sfip_t* sip;
+ const sfip_t* dip;
+
+ dip = pkt->ptrs.ip_api.get_dst();
+ sip = pkt->ptrs.ip_api.get_src();
+ tmp = ntohl(pmr->port);
+ pf = rpc_service_mod.api->flow_new(flowp, pkt, dip, 0, sip, (uint16_t)tmp,
+ // FIXIT-H: Change rd->proto to be IpProtocol
+ (IpProtocol)ntohl(rd->proto), app_id, 0);
+ if (pf)
+ {
+ rpc_service_mod.api->data_add_id(pf, (uint16_t)tmp,
+ flowp->proto==IpProtocol::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));
+ }
+ }
+ 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 = nullptr;
+ int rval;
+ AppIdData* flowp = args->flowp;
+ const uint8_t* data = args->data;
+ Packet* pkt = args->pkt;
+ const int dir = args->dir;
+ uint16_t size = args->size;
+
+ if (!size)
+ {
+ rval = SERVICE_INPROCESS;
+ goto done;
+ }
+
+ rd = (ServiceRPCData*)rpc_service_mod.api->data_get(flowp, rpc_service_mod.flow_data_index);
+ if (!rd)
+ {
+ rd = (ServiceRPCData*)snort_calloc(sizeof(ServiceRPCData));
+ rpc_service_mod.api->data_add(flowp, rd, rpc_service_mod.flow_data_index, &snort_free);
+ 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);
+ }
+ 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 = nullptr;
+ rpc_service_mod.api->add_service(flowp, pkt, dir, &svc_element,
+ APP_ID_SUN_RPC, nullptr, nullptr, subtype);
+ }
+ 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);
+ }
+ 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);
+ }
+ 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;
+ const ServiceRPCCall* call;
+ const ServiceRPCReply* reply;
+
+ static char subname[64];
+ RNAServiceSubtype sub;
+ RNAServiceSubtype* subtype;
+ uint32_t program = 0;
+ const char* pname = nullptr;
+
+ AppIdData* flowp = args->flowp;
+ const uint8_t* data = args->data;
+ Packet* pkt = args->pkt;
+ const int dir = args->dir;
+ uint16_t size = args->size;
+
+ if (!size)
+ goto inprocess;
+
+ rd = (ServiceRPCData*)rpc_service_mod.api->data_get(flowp, rpc_service_mod.flow_data_index);
+ if (!rd)
+ {
+ rd = (ServiceRPCData*)snort_calloc(sizeof(ServiceRPCData));
+ rpc_service_mod.api->data_add(flowp, rd, rpc_service_mod.flow_data_index, &snort_free);
+ rd->state = RPC_STATE_CALL;
+ for (ret=0; ret<APP_ID_APPID_SESSION_DIRECTION_MAX; ret++)
+ {
+ rd->tcpstate[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))
+ {
+ length = ntohl(((ServiceRPCCall*)rd->tcpdata[dir])->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])
+ {
+ ((ServiceRPCCall*)rd->tcpdata[dir])->cred.flavor = 0;
+ ((ServiceRPCCall*)rd->tcpdata[dir])->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)
+ length = ntohl(((ServiceRPCCall*)rd->tcpdata[dir])->verify.length);
+ else
+ length = ntohl(((ServiceRPCReply*)rd->tcpdata[dir])->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)
+ {
+ ((ServiceRPCCall*)rd->tcpdata[dir])->verify.flavor = 0;
+ ((ServiceRPCCall*)rd->tcpdata[dir])->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
+ {
+ ((ServiceRPCReply*)rd->tcpdata[dir])->verify.flavor = 0;
+ ((ServiceRPCReply*)rd->tcpdata[dir])->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);
+ }
+ 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 = nullptr;
+ rpc_service_mod.api->add_service(flowp, pkt, dir, &tcp_svc_element,
+ APP_ID_SUN_RPC, nullptr, nullptr, subtype);
+ }
+ 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);
+ }
+ 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);
+ }
+ 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;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_rpc.h author Sourcefire Inc.
+
+#ifndef SERVICE_RPC_H
+#define SERVICE_RPC_H
+
+struct RNAServiceValidationModule;
+extern RNAServiceValidationModule rpc_service_mod;
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_rshell.cc author Sourcefire Inc.
+
+#include "service_rshell.h"
+
+#include "application_ids.h"
+#include "service_api.h"
+#include "service_base.h"
+#include "app_info_table.h"
+
+#include "log/messages.h"
+#include "main/snort_debug.h"
+#include "target_based/snort_protocols.h"
+#include "utils/util.h"
+
+#define RSHELL_PORT 514
+#define RSHELL_MAX_PORT_PACKET 6
+
+enum RSHELLState
+{
+ RSHELL_STATE_PORT,
+ RSHELL_STATE_SERVER_CONNECT,
+ RSHELL_STATE_USERNAME,
+ RSHELL_STATE_USERNAME2,
+ RSHELL_STATE_COMMAND,
+ RSHELL_STATE_REPLY,
+ RSHELL_STATE_DONE,
+ RSHELL_STATE_STDERR_CONNECT_SYN,
+ RSHELL_STATE_STDERR_CONNECT_SYN_ACK
+};
+
+struct ServiceRSHELLData
+{
+ RSHELLState state;
+ ServiceRSHELLData* parent;
+ ServiceRSHELLData* child;
+};
+
+static int rshell_init(const IniServiceAPI* const init_api);
+static int rshell_validate(ServiceValidationArgs* args);
+
+// FIXIT-L: Make the globals const or, if necessary, thread-local.
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &rshell_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "rshell"
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &rshell_validate, RSHELL_PORT, IpProtocol::TCP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+RNAServiceValidationModule rshell_service_mod =
+{
+ "RSHELL",
+ &rshell_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_SHELL, APPINFO_FLAG_SERVICE_ADDITIONAL }
+};
+
+static int16_t app_id = 0;
+
+static int rshell_init(const IniServiceAPI* const init_api)
+{
+ unsigned i;
+
+ app_id = AddProtocolReference("rsh-error");
+
+ for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++)
+ {
+ DebugFormat(DEBUG_INSPECTOR,"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 = nullptr;
+ rd->parent->parent = nullptr;
+ }
+ if (rd->child)
+ {
+ rd->child->parent = nullptr;
+ rd->child->child = nullptr;
+ }
+ snort_free(rd);
+ }
+}
+
+static int rshell_validate(ServiceValidationArgs* args)
+{
+ ServiceRSHELLData* rd = nullptr;
+ ServiceRSHELLData* tmp_rd;
+ int i;
+ uint32_t port;
+ AppIdData* pf;
+ AppIdData* flowp = args->flowp;
+ const uint8_t* data = args->data;
+ Packet* 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 = (ServiceRSHELLData*)rshell_service_mod.api->data_get(flowp,
+ rshell_service_mod.flow_data_index);
+ if (!rd)
+ {
+ if (!size)
+ goto inprocess;
+ rd = (ServiceRSHELLData*)snort_calloc(sizeof(ServiceRSHELLData));
+ rshell_service_mod.api->data_add(flowp, rd,
+ rshell_service_mod.flow_data_index, &rshell_free_state);
+ rd->state = RSHELL_STATE_PORT;
+ }
+
+ if (app_id_debug_session_flag)
+ LogMessage("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<size-1; i++)
+ {
+ if (!isdigit(data[i]))
+ goto bail;
+ port *= 10;
+ port += data[i] - '0';
+ }
+ if (port > 65535)
+ goto bail;
+ if (port)
+ {
+ const sfip_t* sip;
+ const sfip_t* dip;
+
+ tmp_rd = (ServiceRSHELLData*)snort_calloc(sizeof(ServiceRSHELLData));
+ tmp_rd->state = RSHELL_STATE_STDERR_CONNECT_SYN;
+ tmp_rd->parent = rd;
+ dip = pkt->ptrs.ip_api.get_dst();
+ sip = pkt->ptrs.ip_api.get_src();
+ // FIXIT-M can flow_new return null?
+ pf = rshell_service_mod.api->flow_new(flowp, pkt, dip, 0, sip, (uint16_t)port,
+ IpProtocol::TCP, app_id, APPID_EARLY_SESSION_FLAG_FW_RULE);
+ if (pf)
+ {
+ pf->rnaClientState = RNA_STATE_FINISHED;
+ rshell_service_mod.api->data_add(pf, tmp_rd,
+ rshell_service_mod.flow_data_index, &rshell_free_state);
+ 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 = nullptr;
+ return SERVICE_ENOMEM;
+ }
+ pf->scan_flags |= SCAN_HOST_PORT_FLAG;
+ PopulateExpectedFlow(flowp, pf,
+ APPID_SESSION_NO_TPI |
+ APPID_SESSION_IGNORE_HOST |
+ APPID_SESSION_NOT_A_SERVICE |
+ APPID_SESSION_PORT_SERVICE_DONE);
+ pf->rnaServiceState = RNA_STATE_STATEFUL;
+ }
+ else
+ {
+ snort_free(tmp_rd);
+ return SERVICE_ENOMEM;
+ }
+ rd->child = tmp_rd;
+ rd->state = RSHELL_STATE_SERVER_CONNECT;
+ }
+ 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; i<size && data[i]; i++)
+ if (!isprint(data[i]) || isspace(data[i]))
+ goto bail;
+ rd->state = 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; i<size && data[i]; i++)
+ if (!isprint(data[i]) || isspace(data[i]))
+ goto bail;
+ rd->state = 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; i<size && data[i]; i++)
+ if (!isprint(data[i]))
+ goto bail;
+ rd->state = 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; i<size && data[i]; i++)
+ {
+ if (!isprint(data[i]))
+ goto bail;
+ }
+ i++;
+ if (i != size)
+ goto bail;
+ rd->state = RSHELL_STATE_REPLY;
+ break;
+ case RSHELL_STATE_REPLY:
+ if (!size)
+ goto inprocess;
+ if (dir != APP_ID_FROM_RESPONDER)
+ goto fail;
+ if (size == 1)
+ goto success;
+ if (*data == 0x01)
+ {
+ data++;
+ size--;
+ for (i=0; i<size && data[i]; i++)
+ {
+ if (!isprint(data[i]) && data[i] != 0x0A && data[i] != 0x0D && data[i] != 0x09)
+ goto fail;
+ }
+ goto success;
+ }
+ goto fail;
+ break;
+ 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;
+ setAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED);
+ return SERVICE_SUCCESS;
+ default:
+ goto bail;
+ }
+
+inprocess:
+ rshell_service_mod.api->service_inprocess(flowp, pkt, dir, &svc_element);
+ return SERVICE_INPROCESS;
+
+success:
+ rshell_service_mod.api->add_service(flowp, pkt, dir, &svc_element,
+ APP_ID_SHELL, nullptr, nullptr, nullptr);
+ return SERVICE_SUCCESS;
+
+bail:
+ rshell_service_mod.api->incompatible_data(flowp, pkt, dir, &svc_element,
+ rshell_service_mod.flow_data_index, args->pConfig);
+ return SERVICE_NOT_COMPATIBLE;
+
+fail:
+ rshell_service_mod.api->fail_service(flowp, pkt, dir, &svc_element,
+ rshell_service_mod.flow_data_index, args->pConfig);
+ return SERVICE_NOMATCH;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_rshell.h author Sourcefire Inc.
+
+#ifndef SERVICE_RSHELL_H
+#define SERVICE_RSHELL_H
+
+struct RNAServiceValidationModule;
+// FIXIT-L: Make the globals const or, if necessary, thread-local.
+extern RNAServiceValidationModule rshell_service_mod;
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_rsync.cc author Sourcefire Inc.
+
+#include "service_rsync.h"
+
+#include "application_ids.h"
+#include "service_api.h"
+#include "app_info_table.h"
+
+#include "main/snort_debug.h"
+#include "utils/util.h"
+
+#define RSYNC_PORT 873
+
+#define RSYNC_BANNER "@RSYNCD: "
+
+enum RSYNCState
+{
+ RSYNC_STATE_BANNER,
+ RSYNC_STATE_MOTD,
+ RSYNC_STATE_DONE
+};
+
+struct ServiceRSYNCData
+{
+ RSYNCState state;
+};
+
+static int rsync_init(const IniServiceAPI* const init_api);
+static int rsync_validate(ServiceValidationArgs* args);
+
+// FIXIT-L: Make the globals const or, if necessary, thread-local.
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &rsync_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "rsync"
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &rsync_validate, RSYNC_PORT, IpProtocol::TCP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+RNAServiceValidationModule rsync_service_mod =
+{
+ "RSYNC",
+ &rsync_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_RSYNC, APPINFO_FLAG_SERVICE_ADDITIONAL }
+};
+
+static int rsync_init(const IniServiceAPI* const init_api)
+{
+ init_api->RegisterPattern(&rsync_validate, IpProtocol::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++)
+ {
+ DebugFormat(DEBUG_INSPECTOR,"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;
+ AppIdData* 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 = (ServiceRSYNCData*)rsync_service_mod.api->data_get(flowp,
+ rsync_service_mod.flow_data_index);
+ if (!rd)
+ {
+ rd = (ServiceRSYNCData*)snort_calloc(sizeof(ServiceRSYNCData));
+ rsync_service_mod.api->data_add(flowp, rd, rsync_service_mod.flow_data_index, &snort_free);
+ 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; i<size-1; i++)
+ if (!isdigit(data[i]) && data[i] != '.')
+ goto fail;
+ rd->state = RSYNC_STATE_MOTD;
+ break;
+ case RSYNC_STATE_MOTD:
+ if (data[size-1] != 0x0A)
+ goto fail;
+ for (i=0; i<size-1; i++)
+ if (!isprint(data[i]) && !isspace(data[i]))
+ goto fail;
+ rd->state = RSYNC_STATE_DONE;
+ goto success;
+ default:
+ goto fail;
+ }
+
+inprocess:
+ rsync_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &svc_element);
+ return SERVICE_INPROCESS;
+
+success:
+ rsync_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element,
+ APP_ID_RSYNC, nullptr, nullptr, nullptr);
+ 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);
+ return SERVICE_NOMATCH;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_rsync.h author Sourcefire Inc.
+
+#ifndef SERVICE_RSYNC_H
+#define SERVICE_RSYNC_H
+
+struct RNAServiceValidationModule;
+// FIXIT-L: Make the globals const or, if necessary, thread-local.
+extern RNAServiceValidationModule rsync_service_mod;
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_rtmp.cc author Sourcefire Inc.
+
+#include "service_rtmp.h"
+
+#include "application_ids.h"
+#include "service_api.h"
+#include "app_info_table.h"
+
+#include "log/messages.h"
+#include "main/snort_debug.h"
+#include "utils/util.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)
+
+enum RTMPState
+{
+ 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". */
+};
+
+struct ServiceRTMPData
+{
+ RTMPState client_state;
+ RTMPState server_state;
+ uint16_t client_bytes_left;
+ uint16_t server_bytes_left;
+ char* swfUrl;
+ char* pageUrl;
+};
+
+static int rtmp_init(const IniServiceAPI* const api);
+static int rtmp_validate(ServiceValidationArgs* args);
+
+// FIXIT-L: Make the globals const or, if necessary, thread-local.
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &rtmp_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "rtmp"
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &rtmp_validate, 1935, IpProtocol::TCP, 0 },
+ { &rtmp_validate, 1935, IpProtocol::UDP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+RNAServiceValidationModule rtmp_service_mod =
+{
+ "rtmp",
+ &rtmp_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_RTMP, APPINFO_FLAG_SERVICE_ADDITIONAL }
+};
+
+static int rtmp_init(const IniServiceAPI* const init_api)
+{
+ unsigned i;
+ for (i = 0; i < (sizeof(appIdRegistry) / sizeof(*appIdRegistry)); i++)
+ {
+ DebugFormat(DEBUG_INSPECTOR, "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;
+ snort_free(ss_tmp->swfUrl);
+ snort_free(ss_tmp->pageUrl);
+ snort_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 nullptr;
+ if (data[0] != AMF0_TYPE_STRING)
+ return nullptr;
+ field_len = (data[1] << 8) + data[2];
+ if (field_len == 0)
+ return nullptr;
+ data += 1 + 2;
+ size -= 1 + 2;
+
+ if (size < field_len)
+ return nullptr;
+ str = (char*)snort_alloc(field_len + 1);
+ 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 = nullptr;
+
+ 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 = (uint8_t*)snort_alloc(msg_len);
+ 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 == nullptr)
+ && (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 == nullptr)
+ goto parse_rtmp_message_fail;
+ }
+ else if ( (ss->pageUrl == nullptr)
+ && (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 == nullptr)
+ 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:
+ snort_free(body);
+ return ret;
+
+parse_rtmp_message_fail:
+ ret = 0;
+ goto parse_rtmp_message_done;
+}
+
+static int rtmp_validate(ServiceValidationArgs* args)
+{
+ ServiceRTMPData* ss;
+ AppIdData* flowp = args->flowp;
+ const uint8_t* data = args->data;
+ const int dir = args->dir;
+ uint16_t size = args->size;
+
+ if (!size)
+ goto inprocess;
+
+ ss = (ServiceRTMPData*)rtmp_service_mod.api->data_get(flowp, rtmp_service_mod.flow_data_index);
+ if (!ss)
+ {
+ ss = (ServiceRTMPData*)snort_calloc(sizeof(ServiceRTMPData));
+ rtmp_service_mod.api->data_add(flowp, ss, rtmp_service_mod.flow_data_index, &rtmp_free);
+ }
+
+ /* 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 >= pAppidActiveConfig->mod_config->rtmp_max_packets)
+ {
+ goto fail;
+ }
+
+inprocess:
+ rtmp_service_mod.api->service_inprocess(flowp, args->pkt, dir, &svc_element);
+ return SERVICE_INPROCESS;
+
+fail:
+ snort_free(ss->swfUrl);
+ snort_free(ss->pageUrl);
+ ss->swfUrl = ss->pageUrl = nullptr;
+ rtmp_service_mod.api->fail_service(flowp, args->pkt, dir, &svc_element,
+ rtmp_service_mod.flow_data_index, args->pConfig);
+ return SERVICE_NOMATCH;
+
+success:
+ if (ss->swfUrl != nullptr)
+ {
+ if (!flowp->hsession)
+ flowp->hsession = (httpSession*)snort_calloc(sizeof(httpSession));
+
+ if (flowp->hsession->url == nullptr)
+ {
+ flowp->hsession->url = ss->swfUrl;
+ flowp->scan_flags |= SCAN_HTTP_HOST_URL_FLAG;
+ }
+ else
+ {
+ snort_free(ss->swfUrl);
+ }
+ ss->swfUrl = nullptr;
+ }
+ if (ss->pageUrl != nullptr)
+ {
+ if (!flowp->hsession)
+ flowp->hsession = (httpSession*)snort_calloc(sizeof(httpSession));
+
+ if (!pAppidActiveConfig->mod_config->referred_appId_disabled &&
+ (flowp->hsession->referer == nullptr))
+ flowp->hsession->referer = ss->pageUrl;
+ else
+ snort_free(ss->pageUrl);
+ ss->pageUrl = nullptr;
+ }
+ rtmp_service_mod.api->add_service(flowp, args->pkt, dir, &svc_element,
+ APP_ID_RTMP, nullptr, nullptr, nullptr);
+ return SERVICE_SUCCESS;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_rtmp.h author Sourcefire Inc.
+
+#ifndef SERVICE_RTMP_H
+#define SERVICE_RTMP_H
+
+struct RNAServiceValidationModule;
+// FIXIT-L: Make the globals const or, if necessary, thread-local.
+extern RNAServiceValidationModule rtmp_service_mod;
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_smtp.cc author Sourcefire Inc.
+
+#include "service_smtp.h"
+
+#include "main/snort_debug.h"
+#include "utils/util.h"
+
+#include "service_util.h"
+#include "application_ids.h"
+#include "service_api.h"
+#include "appid_module.h"
+
+#define SMTP_PORT 25
+#define SMTP_CLOSING_CONN "closing connection\x0d\x0a"
+
+enum SMTPState
+{
+ SMTP_STATE_CONNECTION,
+ SMTP_STATE_HELO,
+ SMTP_STATE_TRANSFER,
+ SMTP_STATE_CONNECTION_ERROR,
+ SMTP_STATE_STARTTLS
+};
+
+struct ServiceSMTPData
+{
+ SMTPState state;
+ int code;
+ int multiline;
+ int set_flags;
+};
+
+#pragma pack(1)
+
+struct ServiceSMTPCode
+{
+ uint8_t code[3];
+ uint8_t sp;
+};
+
+#pragma pack()
+
+static int smtp_init(const IniServiceAPI* const init_api);
+static int smtp_validate(ServiceValidationArgs* args);
+
+// FIXIT-L: Make the globals const or, if necessary, thread-local.
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &smtp_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "smtp"
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &smtp_validate, 25, IpProtocol::TCP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+RNAServiceValidationModule smtp_service_mod =
+{
+ "SMTP",
+ &smtp_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_SMTP, 0 },
+ { APP_ID_SMTPS, 0 }
+};
+
+static int smtp_init(const IniServiceAPI* const init_api)
+{
+ const char SMTP_PATTERN1[] = "220 ";
+ const char SMTP_PATTERN2[] = "220-";
+ const char SMTP_PATTERN3[] = "SMTP";
+ const char SMTP_PATTERN4[] = "smtp";
+
+ init_api->RegisterPattern(&smtp_validate, IpProtocol::TCP, (uint8_t*)SMTP_PATTERN1,
+ sizeof(SMTP_PATTERN1) - 1, 0, "smtp", init_api->pAppidConfig);
+ init_api->RegisterPattern(&smtp_validate, IpProtocol::TCP, (uint8_t*)SMTP_PATTERN2,
+ sizeof(SMTP_PATTERN2) - 1, 0, "smtp", init_api->pAppidConfig);
+ init_api->RegisterPattern(&smtp_validate, IpProtocol::TCP, (uint8_t*)SMTP_PATTERN3,
+ sizeof(SMTP_PATTERN3) - 1, -1, "smtp", init_api->pAppidConfig);
+ init_api->RegisterPattern(&smtp_validate, IpProtocol::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++)
+ {
+ DebugFormat(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId);
+ init_api->RegisterAppId(&smtp_validate, appIdRegistry[i].appId,
+ appIdRegistry[i].additionalInfo, init_api->pAppidConfig);
+ }
+
+ return 0;
+}
+
+static 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 (; *offset<size; (*offset)++)
+ {
+ if (data[*offset] != 0x0D && data[*offset] != 0x0A)
+ break;
+ }
+
+ if (size - *offset < (int)sizeof(ServiceSMTPCode))
+ {
+ for (; *offset<size; (*offset)++)
+ {
+ if (!isspace(data[*offset]))
+ return -1;
+ }
+ return 0;
+ }
+
+ code_hdr = (ServiceSMTPCode*)(data + *offset);
+
+ 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] < '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_validate(ServiceValidationArgs* args)
+{
+ ServiceSMTPData* fd;
+ AppIdData* 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;
+
+ fd = (ServiceSMTPData*)smtp_service_mod.api->data_get(flowp, smtp_service_mod.flow_data_index);
+ if (!fd)
+ {
+ fd = (ServiceSMTPData*)snort_calloc(sizeof(ServiceSMTPData));
+ smtp_service_mod.api->data_add(flowp, fd, smtp_service_mod.flow_data_index, &snort_free);
+ fd->state = SMTP_STATE_CONNECTION;
+ }
+ else
+ {
+ if (getAppIdFlag(flowp, APPID_SESSION_ENCRYPTED) && fd->state == SMTP_STATE_TRANSFER)
+ {
+ fd->state = SMTP_STATE_STARTTLS;
+ }
+ }
+
+ if (!fd->set_flags)
+ {
+ fd->set_flags = 1;
+ setAppIdFlag(flowp, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS);
+ }
+
+ offset = 0;
+ while (offset < size)
+ {
+ if (smtp_validate_reply(data, &offset, size, &fd->multiline, &fd->code) < 0)
+ goto fail;
+ if (!fd->code)
+ goto inprocess;
+ switch (fd->state)
+ {
+ case SMTP_STATE_CONNECTION:
+ switch (fd->code)
+ {
+ case 220:
+ fd->state = SMTP_STATE_HELO;
+ break;
+ case 421:
+ if (service_strstr(data, size, (const uint8_t*)SMTP_CLOSING_CONN,
+ sizeof(SMTP_CLOSING_CONN)-1))
+ goto success;
+ case 520:
+ fd->state = SMTP_STATE_CONNECTION_ERROR;
+ break;
+ default:
+ goto fail;
+ }
+ break;
+ case SMTP_STATE_HELO:
+ switch (fd->code)
+ {
+ case 250:
+ fd->state = SMTP_STATE_TRANSFER;
+ break;
+ case 500:
+ case 501:
+ case 504:
+ break;
+ case 421:
+ case 553:
+ fd->state = SMTP_STATE_CONNECTION_ERROR;
+ break;
+ default:
+ goto fail;
+ }
+ break;
+ case SMTP_STATE_STARTTLS:
+ if (fd->code == 220)
+ goto success;
+ /* STARTTLS failed. */
+ clearAppIdFlag(flowp, APPID_SESSION_ENCRYPTED); // not encrypted after all
+ fd->state = SMTP_STATE_HELO; // revert the state
+ break;
+ case SMTP_STATE_TRANSFER:
+ goto success;
+ case SMTP_STATE_CONNECTION_ERROR:
+ default:
+ goto fail;
+ }
+ }
+
+inprocess:
+ smtp_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &svc_element);
+ return SERVICE_INPROCESS;
+
+success:
+ smtp_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element,
+ fd->state == SMTP_STATE_STARTTLS ? APP_ID_SMTPS : APP_ID_SMTP,
+ nullptr, nullptr, nullptr);
+ if (fd->state == SMTP_STATE_STARTTLS)
+ appid_stats.smtps_count++;
+ else
+ appid_stats.smtp_count++;
+
+ 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);
+ return SERVICE_NOMATCH;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_smtp.h author Sourcefire Inc.
+
+#ifndef SERVICE_SMTP_H
+#define SERVICE_SMTP_H
+
+#include "detector_plugins/detector_api.h"
+
+// FIXIT-L: Make the globals const or, if necessary, thread-local.
+extern RNAServiceValidationModule smtp_service_mod;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_snmp.cc author Sourcefire Inc.
+
+#include "service_snmp.h"
+
+#include "log/messages.h"
+#include "target_based/snort_protocols.h"
+#include "utils/util.h"
+
+#include "appid_api.h"
+#include "app_info_table.h"
+#include "service_base.h"
+#include "application_ids.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"
+
+enum SNMPState
+{
+ SNMP_STATE_CONNECTION,
+ SNMP_STATE_RESPONSE,
+ SNMP_STATE_REQUEST,
+ SNMP_STATE_R_RESPONSE,
+ SNMP_STATE_R_REQUEST,
+ SNMP_STATE_ERROR
+};
+
+struct ServiceSNMPData
+{
+ SNMPState state;
+};
+
+enum SNMPPDUType
+{
+ 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
+};
+
+#pragma pack(1)
+
+struct ServiceSNMPHeader
+{
+ uint16_t opcode;
+ union
+ {
+ uint16_t block;
+ uint16_t errorcode;
+ } d;
+};
+
+#pragma pack()
+
+static int snmp_init(const IniServiceAPI* const init_api);
+static int snmp_validate(ServiceValidationArgs* args);
+
+// FIXIT-L: Make the globals const or, if necessary, thread-local.
+static RNAServiceElement svc_element =
+{
+ nullptr,
+ &snmp_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "snmp",
+};
+
+static RNAServiceValidationPort pp[] =
+{
+ { &snmp_validate, SNMP_PORT, IpProtocol::TCP, 0 },
+ { &snmp_validate, SNMP_PORT, IpProtocol::UDP, 0 },
+ { &snmp_validate, 162, IpProtocol::UDP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+RNAServiceValidationModule snmp_service_mod =
+{
+ "SNMP",
+ &snmp_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static const uint8_t SNMP_PATTERN_2[] = { 0x02, 0x01, 0x00, 0x04 };
+static const uint8_t SNMP_PATTERN_3[] = { 0x02, 0x01, 0x01, 0x04 };
+static const uint8_t SNMP_PATTERN_4[] = { 0x02, 0x01, 0x03, 0x30 };
+
+static AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_SNMP, APPINFO_FLAG_SERVICE_UDP_REVERSED|APPINFO_FLAG_SERVICE_ADDITIONAL }
+};
+
+static int16_t app_id = 0;
+
+static int snmp_init(const IniServiceAPI* const init_api)
+{
+ app_id = AddProtocolReference("snmp");
+
+ init_api->RegisterPattern(&snmp_validate, IpProtocol::UDP, SNMP_PATTERN_2,
+ sizeof(SNMP_PATTERN_2), 2, "snmp", init_api->pAppidConfig);
+ init_api->RegisterPattern(&snmp_validate, IpProtocol::UDP, SNMP_PATTERN_3,
+ sizeof(SNMP_PATTERN_3), 2, "snmp", init_api->pAppidConfig);
+ init_api->RegisterPattern(&snmp_validate, IpProtocol::UDP, SNMP_PATTERN_4,
+ sizeof(SNMP_PATTERN_4), 2, "snmp", init_api->pAppidConfig);
+ init_api->RegisterPattern(&snmp_validate, IpProtocol::UDP, SNMP_PATTERN_2,
+ sizeof(SNMP_PATTERN_2), 3, "snmp", init_api->pAppidConfig);
+ init_api->RegisterPattern(&snmp_validate, IpProtocol::UDP, SNMP_PATTERN_3,
+ sizeof(SNMP_PATTERN_3), 3, "snmp", init_api->pAppidConfig);
+ init_api->RegisterPattern(&snmp_validate, IpProtocol::UDP, SNMP_PATTERN_4,
+ sizeof(SNMP_PATTERN_4), 3, "snmp", init_api->pAppidConfig);
+ init_api->RegisterPattern(&snmp_validate, IpProtocol::UDP, SNMP_PATTERN_2,
+ sizeof(SNMP_PATTERN_2), 4, "snmp", init_api->pAppidConfig);
+ init_api->RegisterPattern(&snmp_validate, IpProtocol::UDP, SNMP_PATTERN_3,
+ sizeof(SNMP_PATTERN_3), 4, "snmp", init_api->pAppidConfig);
+ init_api->RegisterPattern(&snmp_validate, IpProtocol::UDP, SNMP_PATTERN_4,
+ sizeof(SNMP_PATTERN_4), 4, "snmp", init_api->pAppidConfig);
+
+ for (unsigned i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++)
+ {
+ DebugFormat(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 && cnt; cnt--, (*data)++)
+ {
+ *length <<= 8;
+ *length |= **data;
+ }
+ if (cnt)
+ return -1;
+ }
+ return 0;
+}
+
+static int snmp_verify_packet(const uint8_t** const data,
+ const uint8_t* const end, uint8_t* const pdu,
+ uint8_t* version_ret)
+{
+ uint32_t overall_length;
+ uint32_t community_length;
+ uint32_t global_length;
+ uint32_t length;
+ uint8_t version;
+ uint8_t cls;
+ const uint8_t* p;
+
+ if (**data != 0x30)
+ return -1;
+ (*data)++;
+ if (*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;
+ AppIdData* pf;
+ uint8_t pdu;
+ const sfip_t* sip;
+ const sfip_t* dip;
+ uint8_t version;
+ const char* version_str = nullptr;
+ AppIdData* flowp = args->flowp;
+ const uint8_t* data = args->data;
+ Packet* 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 = (ServiceSNMPData*)snmp_service_mod.api->data_get(flowp, snmp_service_mod.flow_data_index);
+ if (!sd)
+ {
+ sd = (ServiceSNMPData*)snort_calloc(sizeof(ServiceSNMPData));
+ snmp_service_mod.api->data_add(flowp, sd, snmp_service_mod.flow_data_index, &snort_free);
+ sd->state = SNMP_STATE_CONNECTION;
+ }
+
+ if (snmp_verify_packet(&data, data+size, &pdu, &version))
+ {
+ if (app_id_debug_session_flag)
+ LogMessage("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)
+ LogMessage("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 = pkt->ptrs.ip_api.get_dst();
+ sip = pkt->ptrs.ip_api.get_src();
+ pf = snmp_service_mod.api->flow_new(flowp, pkt, dip, 0, sip, pkt->ptrs.sp, flowp->proto,
+ app_id, 0);
+ if (pf)
+ {
+ tmp_sd = (ServiceSNMPData*)snort_calloc(sizeof(ServiceSNMPData));
+ tmp_sd->state = SNMP_STATE_RESPONSE;
+ snmp_service_mod.api->data_add(pf, tmp_sd,
+ snmp_service_mod.flow_data_index, &snort_free);
+ if (snmp_service_mod.api->data_add_id(pf, pkt->ptrs.dp, &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);
+ pf->rnaServiceState = RNA_STATE_STATEFUL;
+ pf->scan_flags |= SCAN_HOST_PORT_FLAG;
+ pf->common.initiator_ip = *sip;
+ }
+ break;
+ case SNMP_STATE_RESPONSE:
+ 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;
+ 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)
+ {
+ 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);
+ 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 = nullptr;
+ break;
+ }
+ snmp_service_mod.api->add_service(flowp, pkt, dir, &svc_element,
+ APP_ID_SNMP,
+ SNMP_VENDOR_STR, version_str, nullptr);
+ return SERVICE_SUCCESS;
+
+bail:
+ snmp_service_mod.api->incompatible_data(flowp, pkt, dir, &svc_element,
+ snmp_service_mod.flow_data_index,
+ args->pConfig);
+ return SERVICE_NOT_COMPATIBLE;
+
+fail:
+ snmp_service_mod.api->fail_service(flowp, pkt, dir, &svc_element,
+ snmp_service_mod.flow_data_index,
+ args->pConfig);
+ return SERVICE_NOMATCH;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_snmp.h author Sourcefire Inc.
+
+#ifndef SERVICE_SNMP_H
+#define SERVICE_SNMP_H
+
+// SNMP service
+
+#include "detector_plugins/detector_api.h"
+
+// FIXIT-L: Make the globals const or, if necessary, thread-local.
+extern RNAServiceValidationModule snmp_service_mod;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_ssh.cc author Sourcefire Inc.
+
+#include "service_ssh.h"
+#include "app_info_table.h"
+#include "service_base.h"
+#include "application_ids.h"
+
+#include "utils/util.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
+
+enum SSHState
+{
+ SSH_STATE_BANNER,
+ SSH_STATE_KEY,
+ SSH_STATE_DONE
+};
+
+enum SSHHeaderState
+{
+ 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
+};
+
+enum OldSSHHeaderState
+{
+ OLD_SSH_HEADER_BEGIN,
+ OLD_SSH_HEADER_PLEN,
+ OLD_SSH_HEADER_FIND_CODE,
+ OLD_SSH_HEADER_CODE,
+ OLD_SSH_PUBLIC_KEY
+};
+
+struct ServiceSSHData
+{
+ 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;
+};
+
+#pragma pack(1)
+
+struct ServiceSSHKeyString
+{
+ uint32_t len;
+ uint8_t data;
+};
+
+struct ServiceSSHMsg
+{
+ uint32_t len;
+ uint8_t plen;
+ uint8_t code;
+};
+
+struct ServiceSSHKeyExchange
+{
+ ServiceSSHMsg msg;
+ uint8_t cookie[16];
+};
+
+struct ServiceSSHKeyExchangeV1
+{
+ uint32_t len;
+ uint8_t code;
+};
+
+struct ServiceSSHKeyExchangeFinal
+{
+ uint8_t kex_pkt;
+ uint32_t future;
+};
+
+#pragma pack()
+
+static int ssh_init(const IniServiceAPI* const init_api);
+static int ssh_validate(ServiceValidationArgs* args);
+
+static const RNAServiceElement svc_element =
+{
+ nullptr,
+ &ssh_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "ssh",
+};
+
+// FIXIT can this be const? That would require that RNAServiceValidationModule.pp be const which
+// I don't know about. Otherwise we have a thread safety issue here.
+static RNAServiceValidationPort pp[] =
+{
+ { &ssh_validate, SSH_PORT, IpProtocol::TCP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+// FIXIT maybe this can be const, else thread safety issue
+RNAServiceValidationModule ssh_service_mod =
+{
+ "SSH",
+ &ssh_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static const AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_SSH, APPINFO_FLAG_SERVICE_ADDITIONAL }
+};
+
+static int ssh_init(const IniServiceAPI* const init_api)
+{
+ init_api->RegisterPattern(&ssh_validate, IpProtocol::TCP, (uint8_t*)SSH_BANNER,
+ sizeof(SSH_BANNER) - 1, 0, "ssh", init_api->pAppidConfig);
+ for (unsigned i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++)
+ {
+ DebugFormat(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)
+ {
+ snort_free(sd->vendor);
+ sd->vendor = nullptr;
+ }
+ if (sd->version)
+ {
+ snort_free(sd->version);
+ sd->version = nullptr;
+ }
+ snort_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;
+ AppIdData* flowp = args->flowp;
+ const uint8_t* data = args->data;
+ uint16_t size = args->size;
+
+ if (!size)
+ goto inprocess;
+
+ ss = (ServiceSSHData*)ssh_service_mod.api->data_get(flowp, ssh_service_mod.flow_data_index);
+ if (!ss)
+ {
+ ss = (ServiceSSHData*)snort_calloc(sizeof(ServiceSSHData));
+ ssh_service_mod.api->data_add(flowp, ss,
+ ssh_service_mod.flow_data_index, &ssh_free_state);
+ 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;
+ offset++, blen++)
+ {
+ if (data[offset] == '-')
+ break;
+ if (!isprint(data[offset]) || isspace(data[offset]))
+ {
+ goto fail;
+ }
+ }
+ offset++;
+ blen++;
+ if (offset >= size || blen > SSH_MAX_BANNER_LENGTH)
+ {
+ goto fail;
+ }
+ ven = (char*)&data[offset];
+ for (;
+ offset<size && blen<=SSH_MAX_BANNER_LENGTH;
+ offset++, blen++)
+ {
+ if (data[offset] == 0x0D || data[offset] == 0x0A)
+ {
+ if (data[offset] == 0x0D)
+ {
+ if (offset+1 >= 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 = (char*)snort_alloc(len+1);
+ memcpy(ss->vendor, ven, len);
+ ss->vendor[len] = 0;
+ ver++;
+ len = end - ver;
+ ss->version = (char*)snort_alloc(len+1);
+ memcpy(ss->version, ver, len);
+ ss->version[len] = 0;
+ }
+ else
+ {
+ len = end - ven;
+ ss->version = (char*)snort_calloc(len+1);
+ memcpy(ss->version, ven, len);
+ ss->version[len] = 0;
+ }
+ goto inprocess;
+ }
+ else if (!isprint(data[offset]))
+ goto fail;
+ }
+ goto fail;
+ }
+ else
+ {
+ for (; offset<size; offset++)
+ {
+ if (data[offset] == 0x0a)
+ {
+ offset++;
+ break;
+ }
+ }
+ }
+ }
+ break;
+ case SSH_STATE_KEY:
+ switch (ss->ssh_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);
+ 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, nullptr);
+ 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);
+ 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);
+ return SERVICE_NOT_COMPATIBLE;
+
+ default:
+ return retval;
+ }
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_ssh.h author Sourcefire Inc.
+
+#ifndef SERVICE_SSH_H
+#define SERVICE_SSH_H
+
+#include "detector_plugins/detector_api.h"
+
+// FIXIT-L: Make the globals const or, if necessary, thread-local.
+extern RNAServiceValidationModule ssh_service_mod;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_ssl.cc author Sourcefire Inc.
+
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <openssl/x509.h>
+
+#include "appid_flow_data.h"
+#include "service_config.h"
+#include "service_base.h"
+#include "service_ssl.h"
+#include "fw_appid.h"
+
+#include "main/snort_debug.h"
+#include "utils/util.h"
+
+#define SSL_PORT 443
+
+enum SSLContentType
+{
+ SSL_CHANGE_CIPHER = 20,
+ SSL_ALERT = 21,
+ SSL_HANDSHAKE = 22,
+ SSL_APPLICATION_DATA = 23
+};
+
+#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 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
+
+struct MatchedSSLPatterns
+{
+ SSLCertPattern* mpattern;
+ int index;
+ struct MatchedSSLPatterns* next;
+};
+
+enum SSLState
+{
+ SSL_STATE_INITIATE, /* Client initiates. */
+ SSL_STATE_CONNECTION, /* Server responds... */
+ SSL_STATE_HEADER,
+ SSL_STATE_DONE
+};
+
+struct ServiceSSLData
+{
+ SSLState state;
+ int pos;
+ int length;
+ int tot_length;
+ /* From client: */
+ char* host_name;
+ int host_name_strlen;
+ /* While collecting certificates: */
+ unsigned 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;
+ char* org_name;
+ int org_name_strlen;
+};
+
+struct ServiceSSLCertificate
+{
+ X509* cert;
+ uint8_t* common_name_ptr;
+ int common_name_len;
+ uint8_t* org_name_ptr;
+ int org_name_len;
+ struct ServiceSSLCertificate* next;
+};
+
+#pragma pack(1)
+
+struct ServiceSSLV3Hdr /* Actually a TLS Record. */
+{
+ uint8_t type;
+ uint16_t version;
+ uint16_t len;
+};
+
+struct ServiceSSLV3Record /* 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;
+};
+
+struct ServiceSSLV3CertsRecord /* 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 */
+};
+
+struct ServiceSSLV3ExtensionServerName
+{
+ uint16_t type;
+ uint16_t length;
+ uint16_t list_length;
+ uint8_t string_length_msb;
+ uint16_t string_length;
+ /* String follows. */
+};
+
+struct ServiceSSLPCTHdr
+{
+ 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;
+};
+
+struct ServiceSSLV2Hdr
+{
+ 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;
+};
+
+#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*, int index, void* data, void*)
+{
+ MatchedSSLPatterns* cm;
+ MatchedSSLPatterns** matches = (MatchedSSLPatterns**)data;
+ SSLCertPattern* target = (SSLCertPattern*)id;
+
+ cm = (MatchedSSLPatterns*)snort_alloc(sizeof(MatchedSSLPatterns));
+ cm->mpattern = target;
+ cm->index = index;
+ cm->next = *matches;
+ *matches = cm;
+
+ return 0;
+}
+
+static int ssl_detector_create_matcher(SearchTool** matcher, DetectorSSLCertPattern* list)
+{
+ size_t* patternIndex;
+ size_t size = 0;
+ DetectorSSLCertPattern* element = nullptr;
+
+ if (*matcher)
+ delete *matcher;
+
+ if (!(*matcher = new SearchTool("ac_full")))
+ return 0;
+
+ patternIndex = &size;
+
+ /* Add patterns from Lua API */
+ for (element = list; element; element = element->next)
+ {
+ (*matcher)->add(element->dpattern->pattern,
+ element->dpattern->pattern_size,
+ element->dpattern,
+ true);
+ (*patternIndex)++;
+ }
+
+ (*matcher)->prep();
+
+ return 1;
+}
+
+int ssl_detector_process_patterns(ServiceSslConfig* 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 IniServiceAPI* const api);
+static int ssl_validate(ServiceValidationArgs* args);
+
+static const RNAServiceElement svc_element =
+{
+ nullptr,
+ &ssl_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "ssl",
+};
+
+// FIXIT thread safety, can this be const?
+static RNAServiceValidationPort pp[] =
+{
+ { &ssl_validate, 261, IpProtocol::TCP, 0 },
+ { &ssl_validate, 261, IpProtocol::UDP, 0 },
+ { &ssl_validate, 443, IpProtocol::TCP, 0 },
+ { &ssl_validate, 443, IpProtocol::UDP, 0 },
+ { &ssl_validate, 448, IpProtocol::TCP, 0 },
+ { &ssl_validate, 448, IpProtocol::UDP, 0 },
+ { &ssl_validate, 465, IpProtocol::TCP, 0 },
+ { &ssl_validate, 563, IpProtocol::TCP, 0 },
+ { &ssl_validate, 563, IpProtocol::UDP, 0 },
+ { &ssl_validate, 585, IpProtocol::TCP, 0 },
+ { &ssl_validate, 585, IpProtocol::UDP, 0 },
+ { &ssl_validate, 614, IpProtocol::TCP, 0 },
+ { &ssl_validate, 636, IpProtocol::TCP, 0 },
+ { &ssl_validate, 636, IpProtocol::UDP, 0 },
+ { &ssl_validate, 989, IpProtocol::TCP, 0 },
+ { &ssl_validate, 990, IpProtocol::TCP, 0 },
+ { &ssl_validate, 992, IpProtocol::TCP, 0 },
+ { &ssl_validate, 992, IpProtocol::UDP, 0 },
+ { &ssl_validate, 993, IpProtocol::TCP, 0 },
+ { &ssl_validate, 993, IpProtocol::UDP, 0 },
+ { &ssl_validate, 994, IpProtocol::TCP, 0 },
+ { &ssl_validate, 994, IpProtocol::UDP, 0 },
+ { &ssl_validate, 995, IpProtocol::TCP, 0 },
+ { &ssl_validate, 995, IpProtocol::UDP, 0 },
+ { &ssl_validate, 3269, IpProtocol::TCP, 0 },
+ { &ssl_validate, 8305, IpProtocol::TCP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+// FIXIT thread safety, can this be const?
+RNAServiceValidationModule ssl_service_mod =
+{
+ "ssl",
+ &ssl_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static const uint8_t SSL_PATTERN_PCT[] = { 0x02, 0x00, 0x80, 0x01 };
+static const uint8_t SSL_PATTERN3_0[] = { 0x16, 0x03, 0x00 };
+static const uint8_t SSL_PATTERN3_1[] = { 0x16, 0x03, 0x01 };
+static const uint8_t SSL_PATTERN3_2[] = { 0x16, 0x03, 0x02 };
+static const uint8_t SSL_PATTERN3_3[] = { 0x16, 0x03, 0x03 };
+
+static const AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_SSL, APPINFO_FLAG_SERVICE_ADDITIONAL }
+};
+
+static int ssl_init(const IniServiceAPI* const init_api)
+{
+ init_api->RegisterPattern(&ssl_validate, IpProtocol::TCP, SSL_PATTERN_PCT,
+ sizeof(SSL_PATTERN_PCT), 2, "ssl", init_api->pAppidConfig);
+ init_api->RegisterPattern(&ssl_validate, IpProtocol::TCP, SSL_PATTERN3_0,
+ sizeof(SSL_PATTERN3_0), 0, "ssl", init_api->pAppidConfig);
+ init_api->RegisterPattern(&ssl_validate, IpProtocol::TCP, SSL_PATTERN3_1,
+ sizeof(SSL_PATTERN3_1), 0, "ssl", init_api->pAppidConfig);
+ init_api->RegisterPattern(&ssl_validate, IpProtocol::TCP, SSL_PATTERN3_2,
+ sizeof(SSL_PATTERN3_2), 0, "ssl", init_api->pAppidConfig);
+ init_api->RegisterPattern(&ssl_validate, IpProtocol::TCP, SSL_PATTERN3_3,
+ sizeof(SSL_PATTERN3_3), 0, "ssl", init_api->pAppidConfig);
+ for (unsigned i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++)
+ {
+ DebugFormat(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;
+ snort_free(ss_tmp->certs_data);
+ snort_free(ss_tmp->host_name);
+ snort_free(ss_tmp->common_name);
+ snort_free(ss_tmp->org_name);
+ snort_free(ss_tmp);
+}
+
+void parse_client_initiation(const uint8_t* data, uint16_t size, ServiceSSLData* ss)
+{
+ const ServiceSSLV3Hdr* hdr3;
+ const ServiceSSLV3Record* rec;
+ unsigned 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;
+
+ unsigned 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 = (char*)snort_alloc(len + 1); /* Plus nullptr term. */
+ 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;
+ char* org_name;
+ char* common_name_ptr;
+ char* org_name_ptr;
+
+ /* Pull out certificates from block of data. */
+ uint8_t* data = ss->certs_data;
+ int len = ss->certs_len;
+ ServiceSSLCertificate* certs_head = nullptr;
+ ServiceSSLCertificate* certs_curr = nullptr;
+ int common_name_tot_len = 0;
+ int org_name_tot_len = 0;
+ int num_certs = 0;
+ success = 1;
+ while (len > 0)
+ {
+ X509* cert;
+ char* start;
+ char* end;
+ int length;
+
+ /* Get each certificate. */
+ int cert_len = ntoh3(data);
+ data += 3;
+ len -= 3;
+ if (len < cert_len)
+ {
+ success = 0;
+ break;
+ }
+ cert = d2i_X509(nullptr, (const unsigned char**)&data, cert_len);
+ len -= cert_len; /* Above call increments data pointer already. */
+ if (!cert)
+ {
+ success = 0;
+ break;
+ }
+
+ /* Insert certificate entry into list. */
+ certs_curr = (ServiceSSLCertificate*)snort_calloc(sizeof(ServiceSSLCertificate));
+ certs_curr->cert = cert;
+ certs_curr->next = certs_head;
+ certs_head = certs_curr;
+ num_certs++;
+
+ /* Find "common name" value. */
+ start = strstr(cert->name, COMMON_NAME_STR);
+ if (start)
+ {
+ start += strlen(COMMON_NAME_STR);
+ certs_curr->common_name_ptr = (uint8_t*)start;
+ end = strstr(start, FIELD_SEPARATOR);
+ if (end)
+ {
+ length = end - start;
+ }
+ else
+ {
+ length = strlen(start);
+ }
+ certs_curr->common_name_len = length;
+ common_name_tot_len += length;
+ }
+
+ /* Find "org name" value. */
+ start = strstr(cert->name, ORG_NAME_STR);
+ if (start)
+ {
+ start += strlen(ORG_NAME_STR);
+ certs_curr->org_name_ptr = (uint8_t*)start;
+ end = strstr(start, FIELD_SEPARATOR);
+ if (end)
+ {
+ length = end - start;
+ }
+ else
+ {
+ length = strlen(start);
+ }
+ certs_curr->org_name_len = length;
+ org_name_tot_len += length;
+ }
+ }
+ if (!success)
+ {
+ goto parse_certificates_clean;
+ }
+
+ // Build up concatenated string of fields.
+ common_name = nullptr;
+ org_name = nullptr;
+ if (common_name_tot_len)
+ {
+ common_name_tot_len += num_certs; /* Space between each and terminator at end. */
+ common_name = (char*)snort_calloc(common_name_tot_len);
+ }
+
+ if (org_name_tot_len)
+ {
+ org_name_tot_len += num_certs; /* Space between each and terminator at end. */
+ org_name = (char*)snort_calloc(org_name_tot_len);
+ }
+
+ common_name_ptr = common_name;
+ org_name_ptr = org_name;
+ certs_curr = certs_head;
+ while (certs_curr)
+ {
+ /* Grab this common name. */
+ if (certs_curr->common_name_ptr && certs_curr->common_name_len)
+ {
+ memcpy(common_name_ptr, certs_curr->common_name_ptr, certs_curr->common_name_len);
+ common_name_ptr += certs_curr->common_name_len;
+ *common_name_ptr = ' ';
+ common_name_ptr += 1;
+ }
+
+ /* Grab this org name. */
+ if (certs_curr->org_name_ptr && certs_curr->org_name_len)
+ {
+ memcpy(org_name_ptr, certs_curr->org_name_ptr, certs_curr->org_name_len);
+ org_name_ptr += certs_curr->org_name_len;
+ *org_name_ptr = ' ';
+ org_name_ptr += 1;
+ }
+
+ certs_curr = certs_curr->next;
+ }
+ if (common_name_tot_len)
+ {
+ common_name_ptr -= 1;
+ *common_name_ptr = '\0'; /* Put terminator at end rather than space. */
+ }
+ if (org_name_tot_len)
+ {
+ org_name_ptr -= 1;
+ *org_name_ptr = '\0'; /* Put terminator at end rather than space. */
+ }
+ ss->common_name = common_name;
+ ss->common_name_strlen = common_name_tot_len - 1; /* Minus terminator. */
+ ss->org_name = org_name;
+ ss->org_name_strlen = org_name_tot_len - 1; /* Minus terminator. */
+
+parse_certificates_clean:
+
+ while (certs_head)
+ {
+ certs_curr = certs_head;
+ certs_head = certs_head->next;
+ X509_free(certs_curr->cert);
+ snort_free(certs_curr);
+ }
+
+ /* No longer need entire certificates. We have what we came for. */
+ snort_free(ss->certs_data);
+ ss->certs_data = nullptr;
+ ss->certs_len = 0;
+ }
+ return success; /* 1 is OK; 0 is fail. */
+}
+
+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;
+ AppIdData* flowp = args->flowp;
+ const uint8_t* data = args->data;
+ const int dir = args->dir;
+ uint16_t size = args->size;
+
+ if (!size)
+ goto inprocess;
+
+ ss = (ServiceSSLData*)ssl_service_mod.api->data_get(flowp, ssl_service_mod.flow_data_index);
+ if (!ss)
+ {
+ ss = (ServiceSSLData*)snort_calloc(sizeof(ServiceSSLData));
+ ssl_service_mod.api->data_add(flowp, ss, ssl_service_mod.flow_data_index, &ssl_free);
+ 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 (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:
+ ss->state = SSL_STATE_DONE;
+ pct = (ServiceSSLPCTHdr*)data;
+ hdr2 = (ServiceSSLV2Hdr*)data;
+ hdr3 = (ServiceSSLV3Hdr*)data;
+ if (size >= sizeof(ServiceSSLPCTHdr) && pct->len >= 0x80 &&
+ pct->type == PCT_SERVER_HELLO && ntohs(pct->version) == 0x8001)
+ {
+ goto success;
+ }
+ if (size >= sizeof(ServiceSSLV2Hdr) && hdr2->len >= 0x80 &&
+ hdr2->type == SSL2_SERVER_HELLO && !(hdr2->cert & 0xFE))
+ {
+ switch (ntohs(hdr2->version))
+ {
+ case 0x0002:
+ case 0x0300:
+ case 0x0301:
+ case 0x0303:
+ break;
+ default:
+ goto not_v2;
+ }
+ if (hdr2->cipher_len % 3)
+ goto not_v2;
+
+ goto success;
+not_v2:;
+ }
+ 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 (size == ss->length)
+ goto success; /* Just a Server Hello. */
+ 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:
+ ss->state = SSL_STATE_DONE;
+ 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 ||
+ (ver != 0x0300 &&
+ ver != 0x0301 &&
+ ver != 0x0302 &&
+ ver != 0x0303))
+ {
+ goto fail;
+ }
+ data += sizeof(ServiceSSLV3Hdr);
+ size -= sizeof(ServiceSSLV3Hdr);
+ ss->tot_length = ntohs(hdr3->len);
+ }
+
+ rec = (ServiceSSLV3Record*)data;
+ if (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 = (uint8_t*)snort_alloc(ss->certs_len);
+ if ((size - sizeof(ServiceSSLV3CertsRecord)) < ss->certs_len)
+ {
+ /* Will have to get more next time around. */
+ ss->in_certs = 1;
+ // Skip over header to data
+ ss->certs_curr_len = size - sizeof(ServiceSSLV3CertsRecord);
+ 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);
+ goto success; /* We got everything we need. */
+ }
+ }
+ /* fall through */
+ case SSL_SERVER_KEY_XCHG:
+ case SSL_SERVER_CERT_REQ:
+ ss->length = ntohs(rec->length) +
+ offsetof(ServiceSSLV3Record, version);
+ if (size == ss->length)
+ goto success;
+ 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;
+ }
+ ss->state = SSL_STATE_HEADER;
+ break;
+ case SSL_SERVER_HELLO_DONE:
+ 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;
+ goto success; /* We got everything we need. */
+ }
+ }
+
+ 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;
+ }
+ ss->state = SSL_STATE_HEADER;
+ }
+ }
+ break;
+ default:
+ goto fail;
+ }
+
+inprocess:
+ ssl_service_mod.api->service_inprocess(flowp, args->pkt, dir, &svc_element);
+ return SERVICE_INPROCESS;
+
+fail:
+ snort_free(ss->certs_data);
+ snort_free(ss->host_name);
+ snort_free(ss->common_name);
+ snort_free(ss->org_name);
+ ss->certs_data = nullptr;
+ ss->host_name = ss->common_name = ss->org_name = nullptr;
+ ssl_service_mod.api->fail_service(flowp, args->pkt, dir, &svc_element,
+ ssl_service_mod.flow_data_index, args->pConfig);
+ return SERVICE_NOMATCH;
+
+success:
+ if (ss->certs_data && ss->certs_len)
+ {
+ if (!parse_certificates(ss))
+ {
+ goto fail;
+ }
+ }
+ setAppIdFlag(flowp, APPID_SESSION_SSL_SESSION);
+ if (ss->host_name || ss->common_name || ss->org_name)
+ {
+ if (!flowp->tsession)
+ flowp->tsession = (tlsSession*)snort_calloc(sizeof(tlsSession));
+
+ /* TLS Host */
+ if (ss->host_name)
+ {
+ if (flowp->tsession->tls_host)
+ snort_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 = snort_strdup(ss->common_name);
+
+ if (flowp->tsession->tls_host)
+ snort_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)
+ snort_free(flowp->tsession->tls_cname);
+ flowp->tsession->tls_cname = ss->common_name;
+ flowp->tsession->tls_cname_strlen = ss->common_name_strlen;
+ }
+
+ /* TLS Org Unit */
+ if (ss->org_name)
+ {
+ if (flowp->tsession->tls_orgUnit)
+ snort_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 = nullptr;
+ }
+ ssl_service_mod.api->add_service(flowp, args->pkt, dir, &svc_element,
+ getSslServiceAppId(args->pkt->ptrs.sp), nullptr, nullptr, nullptr);
+ return SERVICE_SUCCESS;
+}
+
+AppId 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 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(AppId 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(SearchTool* matcher, const u_int8_t* pattern, size_t size,
+ AppId* ClientAppId, AppId* payloadId)
+{
+ MatchedSSLPatterns* mp = nullptr;
+ MatchedSSLPatterns* tmpMp;
+ SSLCertPattern* best_match;
+
+ if (!matcher)
+ return 0;
+
+ matcher->find_all((char*)pattern, size, ssl_cert_pattern_match, false, &mp);
+
+ if (!mp)
+ return 0;
+
+ best_match = nullptr;
+ 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, AppId* ClientAppId, AppId* payloadId,
+ ServiceSslConfig* 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, AppId* ClientAppId, AppId* payloadId,
+ ServiceSslConfig* pSslConfig)
+{
+ return ssl_scan_patterns(pSslConfig->ssl_cname_matcher, pattern, size, ClientAppId, payloadId);
+}
+
+void service_ssl_clean(ServiceSslConfig* pSslConfig)
+{
+ if (pSslConfig->ssl_host_matcher)
+ {
+ delete pSslConfig->ssl_host_matcher;
+ pSslConfig->ssl_host_matcher = nullptr;
+ }
+ if (pSslConfig->ssl_cname_matcher)
+ {
+ delete pSslConfig->ssl_cname_matcher;
+ pSslConfig->ssl_cname_matcher = nullptr;
+ }
+}
+
+static int ssl_add_pattern(DetectorSSLCertPattern** list, uint8_t* pattern_str, size_t
+ pattern_size, uint8_t type, AppId app_id)
+{
+ DetectorSSLCertPattern* new_ssl_pattern;
+
+ new_ssl_pattern = (DetectorSSLCertPattern*)snort_calloc(sizeof(DetectorSSLCertPattern));
+ new_ssl_pattern->dpattern = (SSLCertPattern*)snort_calloc(sizeof(SSLCertPattern));
+ 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, AppId app_id,
+ ServiceSslConfig* 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, AppId app_id,
+ ServiceSslConfig* 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)
+ snort_free(tmp_pattern->dpattern->pattern);
+ free (tmp_pattern->dpattern);
+ }
+ snort_free(tmp_pattern);
+ }
+}
+
+void ssl_detector_free_patterns(ServiceSslConfig* pSslConfig)
+{
+ ssl_patterns_free(&pSslConfig->DetectorSSLCertPatternList);
+ ssl_patterns_free(&pSslConfig->DetectorSSLCnamePatternList);
+}
+
+int setSSLSquelch(Packet* p, int type, AppId appId)
+{
+ const sfip_t* sip;
+ const sfip_t* dip;
+ AppIdData* f;
+
+ if (!appInfoEntryFlagGet(appId, APPINFO_FLAG_SSL_SQUELCH, pAppidActiveConfig))
+ return 0;
+
+ dip = p->ptrs.ip_api.get_dst();
+ sip = p->ptrs.ip_api.get_src();
+
+ if (!(f = AppIdEarlySessionCreate(nullptr, p, sip, 0, dip, p->ptrs.dp,
+ IpProtocol::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;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_ssl.h author Sourcefire Inc.
+
+#ifndef SERVICE_SSL_H
+#define SERVICE_SSL_H
+
+#include "detector_plugins/detector_api.h"
+#include "service_config.h"
+
+extern struct RNAServiceValidationModule ssl_service_mod;
+AppId getSslServiceAppId(short srcPort);
+bool isSslServiceAppId(AppId appId);
+void service_ssl_clean(ServiceSslConfig*);
+int ssl_detector_process_patterns(ServiceSslConfig*);
+int ssl_scan_hostname(const u_int8_t*, size_t, AppId*, AppId*, ServiceSslConfig*);
+int ssl_scan_cname(const u_int8_t*, size_t, AppId*, AppId*, ServiceSslConfig*);
+int ssl_add_cert_pattern(uint8_t*, size_t, uint8_t, AppId, ServiceSslConfig*);
+int ssl_add_cname_pattern(uint8_t*, size_t, uint8_t, AppId, ServiceSslConfig*);
+void ssl_detector_free_patterns(ServiceSslConfig*);
+int setSSLSquelch(Packet* p, int type, AppId appId);
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_telnet.cc author Sourcefire Inc.
+
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include "main/snort_debug.h"
+#include "utils/util.h"
+
+#include "appid_flow_data.h"
+#include "application_ids.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
+
+enum TELNET_COMMAND_VALUE
+{
+ 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
+};
+
+struct ServiceTelnetData
+{
+ unsigned count;
+};
+
+static int telnet_init(const IniServiceAPI* const init_api);
+static int telnet_validate(ServiceValidationArgs* args);
+
+static const RNAServiceElement svc_element =
+{
+ nullptr,
+ &telnet_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "telnet",
+};
+
+// FIXIT thread safety, can this be const?
+static RNAServiceValidationPort pp[] =
+{
+ { &telnet_validate, 23, IpProtocol::TCP, 0 },
+ { &telnet_validate, 23, IpProtocol::UDP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+// FIXIT thread safety, can this be const?
+RNAServiceValidationModule telnet_service_mod =
+{
+ "TELNET",
+ &telnet_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static const AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_TELNET, 0 }
+};
+
+static int telnet_init(const IniServiceAPI* const init_api)
+{
+ for (unsigned i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++)
+ {
+ DebugFormat(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;
+ AppIdData* 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 = (ServiceTelnetData*)telnet_service_mod.api->data_get(flowp,
+ telnet_service_mod.flow_data_index);
+ if (!td)
+ {
+ td = (ServiceTelnetData*)snort_calloc(sizeof(ServiceTelnetData));
+ telnet_service_mod.api->data_add(flowp, td, telnet_service_mod.flow_data_index,
+ &snort_free);
+ }
+
+ for (end=(data+size); data<end; data++)
+ {
+ /* Currently we only look for the first packet to contain
+ wills, won'ts, dos, and don'ts */
+ if (*data != TELNET_CMD_IAC)
+ goto fail;
+ data++;
+ if (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);
+ return SERVICE_INPROCESS;
+
+success:
+ telnet_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element,
+ APP_ID_TELNET, nullptr, nullptr, nullptr);
+ 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);
+ return SERVICE_NOMATCH;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_telnet.h author Sourcefire Inc.
+
+#ifndef SERVICE_TELNET_H
+#define SERVICE_TELNET_H
+
+#include "detector_plugins/detector_api.h"
+
+extern RNAServiceValidationModule telnet_service_mod;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_tftp.cc author Sourcefire Inc.
+
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include "main/snort_debug.h"
+#include "log/messages.h"
+#include "target_based/snort_protocols.h"
+#include "utils/util.h"
+
+#include "app_info_table.h"
+#include "appid_api.h"
+#include "appid_flow_data.h"
+#include "application_ids.h"
+#include "service_api.h"
+#include "service_base.h"
+
+#define TFTP_PORT 69
+
+#define TFTP_COUNT_THRESHOLD 1
+#define TFTP_MAX_PACKET_SIZE 512
+
+enum TFTPState
+{
+ TFTP_STATE_CONNECTION,
+ TFTP_STATE_TRANSFER,
+ TFTP_STATE_ACK,
+ TFTP_STATE_DATA,
+ TFTP_STATE_ERROR
+};
+
+struct ServiceTFTPData
+{
+ TFTPState state;
+ unsigned count;
+ int last;
+ uint16_t block;
+};
+
+#pragma pack(1)
+
+struct ServiceTFTPHeader
+{
+ uint16_t opcode;
+ union
+ {
+ uint16_t block;
+ uint16_t errorcode;
+ } d;
+};
+
+#pragma pack()
+
+static int tftp_init(const IniServiceAPI* const api);
+static int tftp_validate(ServiceValidationArgs* args);
+
+static const RNAServiceElement svc_element =
+{
+ nullptr,
+ &tftp_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "tftp",
+};
+
+// FIXIT thread safety, can this be const?
+static RNAServiceValidationPort pp[] =
+{
+ { &tftp_validate, 69, IpProtocol::UDP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+// FIXIT thread safety, can this be const?
+RNAServiceValidationModule tftp_service_mod =
+{
+ "TFTP",
+ &tftp_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static const AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_TFTP, APPINFO_FLAG_SERVICE_ADDITIONAL }
+};
+
+static int16_t app_id = 0;
+
+static int tftp_init(const IniServiceAPI* const init_api)
+{
+ app_id = AddProtocolReference("tftp");
+ for (unsigned i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++)
+ {
+ DebugFormat(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId);
+ init_api->RegisterAppId(&tftp_validate, appIdRegistry[i].appId,
+ appIdRegistry[i].additionalInfo, init_api->pAppidConfig);
+ }
+
+ return 0;
+}
+
+static int tftp_verify_header(const uint8_t* data, uint16_t size,
+ uint16_t* block)
+{
+ if (size < sizeof(ServiceTFTPHeader))
+ return -1;
+ const ServiceTFTPHeader* 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;
+ AppIdData* pf;
+ const sfip_t* sip;
+ const sfip_t* dip;
+ AppIdData* flowp = args->flowp;
+ const uint8_t* data = args->data;
+ Packet* 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 = (ServiceTFTPData*)tftp_service_mod.api->data_get(flowp, tftp_service_mod.flow_data_index);
+ if (!td)
+ {
+ td = (ServiceTFTPData*)snort_calloc(sizeof(ServiceTFTPData));
+ tftp_service_mod.api->data_add(flowp, td, tftp_service_mod.flow_data_index, &snort_free);
+ td->state = TFTP_STATE_CONNECTION;
+ }
+ if (args->app_id_debug_session_flag)
+ LogMessage("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 = (ServiceTFTPData*)snort_calloc(sizeof(ServiceTFTPData));
+ tmp_td->state = TFTP_STATE_TRANSFER;
+ dip = pkt->ptrs.ip_api.get_dst();
+ sip = pkt->ptrs.ip_api.get_src();
+ pf = tftp_service_mod.api->flow_new(flowp, pkt, dip, 0, sip, pkt->ptrs.sp, flowp->proto,
+ app_id, APPID_EARLY_SESSION_FLAG_FW_RULE);
+ if (pf)
+ {
+ tftp_service_mod.api->data_add(pf, tmp_td,
+ tftp_service_mod.flow_data_index, &snort_free);
+ if (tftp_service_mod.api->data_add_id(pf, pkt->ptrs.dp, &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);
+ pf->common.initiator_ip = *sip;
+ pf->rnaServiceState = RNA_STATE_STATEFUL;
+ pf->scan_flags |= SCAN_HOST_PORT_FLAG;
+ }
+ else
+ {
+ snort_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)
+ LogMessage("AppIdDbg %s tftp failed to verify\n", app_id_debug_session);
+ goto fail;
+ }
+ if (args->app_id_debug_session_flag)
+ LogMessage("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)
+ LogMessage("AppIdDbg %s tftp failed to verify\n", app_id_debug_session);
+ goto bail;
+ }
+ }
+ if (args->app_id_debug_session_flag)
+ LogMessage("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)
+ LogMessage("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);
+ return SERVICE_INPROCESS;
+
+success:
+ if (args->app_id_debug_session_flag)
+ LogMessage("AppIdDbg %s tftp success\n", app_id_debug_session);
+ tftp_service_mod.api->add_service(flowp, pkt, dir, &svc_element,
+ APP_ID_TFTP, nullptr, nullptr, nullptr);
+ return SERVICE_SUCCESS;
+
+bail:
+ tftp_service_mod.api->incompatible_data(flowp, pkt, dir, &svc_element,
+ tftp_service_mod.flow_data_index, args->pConfig);
+ return SERVICE_NOT_COMPATIBLE;
+
+fail:
+ tftp_service_mod.api->fail_service(flowp, pkt, dir, &svc_element,
+ tftp_service_mod.flow_data_index, args->pConfig);
+ return SERVICE_NOMATCH;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_tftp.h author Sourcefire Inc.
+
+#ifndef SERVICE_TFTP_H
+#define SERVICE_TFTP_H
+
+#include "detector_plugins/detector_api.h"
+
+extern RNAServiceValidationModule tftp_service_mod;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_timbuktu.cc author Sourcefire Inc.
+
+#include "appid_flow_data.h"
+#include "application_ids.h"
+#include "service_api.h"
+
+#include "main/snort_debug.h"
+#include "utils/util.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)
+
+enum TIMBUKTUState
+{
+ TIMBUKTU_STATE_BANNER,
+ TIMBUKTU_STATE_MESSAGE_LEN,
+ TIMBUKTU_STATE_MESSAGE_DATA
+};
+
+struct ServiceTIMBUKTUData
+{
+ TIMBUKTUState state;
+ unsigned stringlen;
+ unsigned pos;
+};
+
+#pragma pack(1)
+struct ServiceTIMBUKTUMsg
+{
+ uint16_t any;
+ uint8_t res;
+ uint8_t len;
+ uint8_t message;
+};
+#pragma pack()
+
+static int timbuktu_init(const IniServiceAPI* const init_api);
+static int timbuktu_validate(ServiceValidationArgs* args);
+
+static const RNAServiceElement svc_element =
+{
+ nullptr,
+ &timbuktu_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "timbuktu"
+};
+
+// FIXIT thread safety, can this be const?
+static RNAServiceValidationPort pp[] =
+{
+ { &timbuktu_validate, TIMBUKTU_PORT, IpProtocol::TCP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+// FIXIT thread safety, can this be const?
+SO_PUBLIC RNAServiceValidationModule timbuktu_service_mod =
+{
+ svc_name,
+ &timbuktu_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static const AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_TIMBUKTU, 0 }
+};
+
+static int timbuktu_init(const IniServiceAPI* const init_api)
+{
+ init_api->RegisterPattern(&timbuktu_validate, IpProtocol::TCP, (const
+ u_int8_t*)TIMBUKTU_BANNER,
+ sizeof(TIMBUKTU_BANNER)-1, 0, svc_name, init_api->pAppidConfig);
+ for (unsigned i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++)
+ {
+ DebugFormat(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;
+ AppIdData* 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 = (ServiceTIMBUKTUData*)timbuktu_service_mod.api->data_get(flowp,
+ timbuktu_service_mod.flow_data_index);
+ if (!ss)
+ {
+ ss = (ServiceTIMBUKTUData*)snort_calloc(sizeof(ServiceTIMBUKTUData));
+ timbuktu_service_mod.api->data_add(flowp, ss,
+ timbuktu_service_mod.flow_data_index, &snort_free);
+ 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);
+ return SERVICE_INPROCESS;
+
+success:
+ timbuktu_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element,
+ APP_ID_TIMBUKTU, nullptr, nullptr, nullptr);
+ 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);
+ return SERVICE_NOMATCH;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_tns.cc author Sourcefire Inc.
+
+#include "app_info_table.h"
+#include "appid_flow_data.h"
+#include "application_ids.h"
+#include "service_api.h"
+
+#include "main/snort_debug.h"
+#include "utils/util.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
+
+enum TNSState
+{
+ 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
+};
+
+#define ACCEPT_VERSION_OFFSET 8
+#define MAX_VERSION_SIZE 12
+struct ServiceTNSData
+{
+ TNSState state;
+ unsigned stringlen;
+ unsigned pos;
+ unsigned message;
+ union
+ {
+ uint16_t len;
+ uint8_t raw_len[2];
+ } l;
+ const char* version;
+};
+
+#pragma pack(1)
+struct ServiceTNSMsg
+{
+ uint16_t len;
+ uint16_t checksum;
+ uint8_t msg;
+ uint8_t res;
+ uint16_t hdchecksum;
+ uint8_t data;
+};
+#pragma pack()
+
+static int tns_init(const IniServiceAPI* const init_api);
+static int tns_validate(ServiceValidationArgs* args);
+
+static const RNAServiceElement svc_element =
+{
+ nullptr,
+ &tns_validate,
+ nullptr,
+ DETECTOR_TYPE_DECODER,
+ 1,
+ 1,
+ 0,
+ "tns"
+};
+
+// FIXIT thread safety, can this be const?
+static RNAServiceValidationPort pp[] =
+{
+ { &tns_validate, TNS_PORT, IpProtocol::TCP, 0 },
+ { nullptr, 0, IpProtocol::PROTO_NOT_SET, 0 }
+};
+
+// FIXIT thread safety, can this be const?
+SO_PUBLIC RNAServiceValidationModule tns_service_mod =
+{
+ svc_name,
+ &tns_init,
+ pp,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ 0
+};
+
+static const AppRegistryEntry appIdRegistry[] =
+{
+ { APP_ID_ORACLE_TNS, APPINFO_FLAG_SERVICE_ADDITIONAL },
+};
+
+static int tns_init(const IniServiceAPI* const init_api)
+{
+ init_api->RegisterPattern(&tns_validate, IpProtocol::TCP, (const uint8_t*)TNS_BANNER,
+ TNS_BANNER_LEN, 2, svc_name, init_api->pAppidConfig);
+ for (unsigned i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++)
+ {
+ DebugFormat(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;
+ AppIdData* 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 = (ServiceTNSData*)tns_service_mod.api->data_get(flowp, tns_service_mod.flow_data_index);
+ if (!ss)
+ {
+ ss = (ServiceTNSData*)snort_calloc(sizeof(ServiceTNSData));
+ tns_service_mod.api->data_add(flowp, ss, tns_service_mod.flow_data_index, &snort_free);
+ 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);
+ return SERVICE_INPROCESS;
+
+success:
+ tns_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element, APP_ID_ORACLE_TNS,
+ nullptr, ss->version ? ss->version : nullptr, nullptr);
+ 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);
+ return SERVICE_NOMATCH;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_util.h author Sourcefire Inc.
+
+#ifndef SERVICE_UTIL_H
+#define SERVICE_UTIL_H
+
+#include <stdint.h>
+#include <string.h>
+
+inline const uint8_t* service_strstr(const uint8_t* haystack, unsigned haystack_len,
+ const uint8_t* needle, unsigned needle_len)
+{
+ const uint8_t* h_end = haystack + haystack_len;
+
+ for (const uint8_t* p = haystack; h_end-p >= (int)needle_len; p++)
+ {
+ if (memcmp(p, needle, needle_len) == 0)
+ {
+ return p;
+ }
+ }
+ return nullptr;
+}
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_state.cc author Sourcefire Inc.
+
+#include "service_state.h"
+
+#include "hash/sfxhash.h"
+#include "log/messages.h"
+#include "service_plugins/service_base.h"
+#include "sfip/sf_ip.h"
+
+/*#define DEBUG_SERVICE_STATE 1*/
+
+static SFXHASH* serviceStateCache4;
+static SFXHASH* serviceStateCache6;
+
+#define SERVICE_STATE_CACHE_ROWS 65536
+
+static int AppIdServiceStateFree(void*, void* data)
+{
+ AppIdServiceIDState* id_state = (AppIdServiceIDState*)data;
+ if (id_state->serviceList)
+ {
+ AppIdFreeServiceMatchList(id_state->serviceList);
+ id_state->serviceList = nullptr;
+ }
+
+ return 0;
+}
+
+int AppIdServiceStateInit(unsigned long memcap)
+{
+ serviceStateCache4 = sfxhash_new(SERVICE_STATE_CACHE_ROWS,
+ sizeof(AppIdServiceStateKey4),
+ sizeof(AppIdServiceIDState),
+ memcap >> 1,
+ 1,
+ &AppIdServiceStateFree,
+ &AppIdServiceStateFree,
+ 1);
+ if (!serviceStateCache4)
+ {
+ ErrorMessage("Failed to allocate a hash table");
+ return -1;
+ }
+ serviceStateCache6 = sfxhash_new(SERVICE_STATE_CACHE_ROWS,
+ sizeof(AppIdServiceStateKey6),
+ sizeof(AppIdServiceIDState),
+ memcap >> 1,
+ 1,
+ &AppIdServiceStateFree,
+ &AppIdServiceStateFree,
+ 1);
+ if (!serviceStateCache6)
+ {
+ ErrorMessage("Failed to allocate a hash table");
+ return -1;
+ }
+ return 0;
+}
+
+void AppIdServiceStateCleanup(void)
+{
+ if (serviceStateCache4)
+ {
+ sfxhash_delete(serviceStateCache4);
+ serviceStateCache4 = nullptr;
+ }
+ if (serviceStateCache6)
+ {
+ sfxhash_delete(serviceStateCache6);
+ serviceStateCache6 = nullptr;
+ }
+}
+
+void AppIdRemoveServiceIDState(const sfip_t* ip, IpProtocol proto, uint16_t port, uint32_t level)
+{
+ AppIdServiceStateKey k;
+ SFXHASH* cache;
+
+ if (sfip_family(ip) == AF_INET6)
+ {
+ k.key6.proto = proto;
+ k.key6.port = port;
+ memcpy(k.key6.ip, ip->ip32, sizeof(k.key6.ip));
+ k.key6.level = level;
+ cache = serviceStateCache6;
+ }
+ else
+ {
+ k.key4.proto = proto;
+ k.key4.port = port;
+ k.key4.ip = ip->ip32[0];
+ k.key4.level = level;
+ cache = serviceStateCache4;
+ }
+ if (sfxhash_remove(cache, &k) != SFXHASH_OK)
+ {
+ char ipstr[INET6_ADDRSTRLEN];
+
+ ipstr[0] = 0;
+ sfip_ntop(ip, ipstr, sizeof(ipstr));
+ ErrorMessage("Failed to remove from hash: %s:%u:%u\n",ipstr, (unsigned)proto,
+ (unsigned)port);
+ }
+}
+
+AppIdServiceIDState* AppIdGetServiceIDState(const sfip_t* ip, IpProtocol proto,
+ uint16_t port, uint32_t level)
+{
+ AppIdServiceStateKey k;
+ SFXHASH* cache;
+ AppIdServiceIDState* ss;
+
+ if (sfip_family(ip) == AF_INET6)
+ {
+ k.key6.proto = proto;
+ k.key6.port = port;
+ memcpy(k.key6.ip, ip->ip32, sizeof(k.key6.ip));
+ k.key6.level = level;
+ cache = serviceStateCache6;
+ }
+ else
+ {
+ k.key4.proto = proto;
+ k.key4.port = port;
+ k.key4.ip = ip->ip32[0];
+ k.key4.level = level;
+ cache = serviceStateCache4;
+ }
+ ss = (AppIdServiceIDState*)sfxhash_find(cache, &k);
+
+#ifdef DEBUG_SERVICE_STATE
+ char ipstr[INET6_ADDRSTRLEN];
+
+ ipstr[0] = 0;
+ sfip_ntop(ip, ipstr, sizeof(ipstr));
+ LogMessage("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 : nullptr);
+#endif
+
+ if (ss && ss->svc && !ss->svc->ref_count)
+ {
+ ss->svc = nullptr;
+ ss->state = SERVICE_ID_NEW;
+ }
+
+ return ss;
+}
+
+AppIdServiceIDState* AppIdAddServiceIDState(const sfip_t* ip, IpProtocol proto, uint16_t port,
+ uint32_t
+ level)
+{
+ AppIdServiceStateKey k;
+ AppIdServiceIDState* ss;
+ SFXHASH* cache;
+ char ipstr[INET6_ADDRSTRLEN];
+
+ if (sfip_family(ip) == AF_INET6)
+ {
+ k.key6.proto = proto;
+ k.key6.port = port;
+ memcpy(k.key6.ip, ip->ip32, sizeof(k.key6.ip));
+ k.key6.level = level;
+ cache = serviceStateCache6;
+ }
+ else
+ {
+ k.key4.proto = proto;
+ k.key4.port = port;
+ k.key4.ip = ip->ip32[0];
+ k.key4.level = level;
+ cache = serviceStateCache4;
+ }
+#ifdef DEBUG_SERVICE_STATE
+ ipstr[0] = 0;
+ sfip_ntop(ip, ipstr, sizeof(ipstr));
+#endif
+ if ((sfxhash_add_return_data_ptr(cache, &k, (void**)&ss) < 0) || !ss)
+ {
+ ipstr[0] = 0;
+ sfip_ntop(ip, ipstr, sizeof(ipstr));
+ ErrorMessage("ServiceState: Failed to add to hash: %s:%u:%u:%u\n",ipstr, (unsigned)proto,
+ (unsigned)port, level);
+ return nullptr;
+ }
+#ifdef DEBUG_SERVICE_STATE
+ LogMessage("ServiceState: Added to hash: %s:%u:%u:%u %p\n",ipstr, (unsigned)proto,
+ (unsigned)port, level, ss);
+#endif
+ return ss;
+}
+
+void AppIdServiceStateDumpStats(void)
+{
+ LogMessage("Service State:\n");
+ if (serviceStateCache4)
+ {
+ LogMessage(" IPv4 Count: %u\n", sfxhash_count(serviceStateCache4));
+ LogMessage(" IPv4 Memory Limit: %lu\n", serviceStateCache4->mc.memcap);
+ LogMessage(" IPv4 Memory Used: %lu\n", serviceStateCache4->mc.memused);
+ }
+ if (serviceStateCache6)
+ {
+ LogMessage(" IPv6 Count: %u\n", sfxhash_count(serviceStateCache6));
+ LogMessage(" IPv6 Memory Limit: %lu\n", serviceStateCache6->mc.memcap);
+ LogMessage(" IPv6 Memory Used: %lu\n", serviceStateCache6->mc.memused);
+ }
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// service_state.h author Sourcefire Inc.
+
+#ifndef SERVICE_STATE_H
+#define SERVICE_STATE_H
+
+#include "sfip/sfip_t.h"
+
+struct RNAServiceElement;
+struct ServiceMatch;
+enum class IpProtocol : uint8_t;
+
+// Service state stored in hosttracker for maintaining service matching states.
+enum SERVICE_ID_STATE
+{
+ /**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 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,
+
+ /**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,
+};
+
+#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
+
+// Service state saved in hosttracker, for identifying a service across multiple flow instances.
+struct AppIdServiceIDState
+{
+ const RNAServiceElement* svc;
+
+ /**State of service identification.*/
+ SERVICE_ID_STATE state;
+ unsigned valid_count;
+ unsigned detract_count;
+ sfip_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.
+ */
+ sfip_t last_invalid_client;
+
+ /** Count for number of unknown sessions saved
+ */
+ unsigned unknowns_logged;
+ time_t reset_time;
+
+ /**List of ServiceMatch nodes which are sorted in order of pattern match. The list is contructed
+ * once on first packet from server and then used for subsequent flows. This saves repeat pattern
+ * matching, but has the disadvantage of making one flow match dependent on first instance of the
+ * same flow.
+ */
+ ServiceMatch* serviceList;
+ ServiceMatch* currenService;
+
+ /** Is this entry currently being used in an active session? */
+ bool searching;
+};
+
+struct AppIdServiceStateKey4
+{
+ uint16_t port;
+ IpProtocol proto;
+ uint32_t ip;
+ uint32_t level;
+};
+
+struct AppIdServiceStateKey6
+{
+ uint16_t port;
+ IpProtocol proto;
+ uint8_t ip[16];
+ uint32_t level;
+};
+
+union AppIdServiceStateKey
+{
+ AppIdServiceStateKey4 key4;
+ AppIdServiceStateKey6 key6;
+};
+
+int AppIdServiceStateInit(unsigned long memcap);
+void AppIdServiceStateCleanup();
+void AppIdRemoveServiceIDState(sfip_t*, IpProtocol proto, uint16_t port, uint32_t level);
+AppIdServiceIDState* AppIdGetServiceIDState( const sfip_t*, IpProtocol proto, uint16_t port, uint32_t level);
+AppIdServiceIDState* AppIdAddServiceIDState( const sfip_t*, IpProtocol proto, uint16_t port, uint32_t level);
+void AppIdServiceStateDumpStats();
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// sfaddr_temp.h author Sourcefire Inc.
+
+#ifndef SFADDR_TEMP_H
+#define SFADDR_TEMP_H
+
+#include "protocols/ipv6.h"
+
+#define WORKAROUND_UNTIL_SFIP_CHANGES_FROM_SNORT299_ARE_PORTED_TO_SNORT3
+#ifdef WORKAROUND_UNTIL_SFIP_CHANGES_FROM_SNORT299_ARE_PORTED_TO_SNORT3
+
+#define sfaddr_get_ip4_value(x) (0)
+#define sfaddr_get_ptr(x) (0)
+#define sfip_fast_eq6(x,y) (0)
+#define sfip_fast_equals_raw(x, y) (0)
+#define sfaddr_family(x) ((x)->family)
+
+#endif
+
+#endif
--- /dev/null
+set (
+ APPID_LIBS
+ appid
+)
+
+add_cpputest(appid_simple_test appid)
+#add_cpputest(host_cache_module_test host_tracker ${HOST_TRACKER_MODULE_LIBS} hash)
+#add_cpputest(host_tracker_module_test host_tracker ${HOST_TRACKER_MODULE_LIBS})
+#add_cpputest(host_tracker_test host_tracker)
--- /dev/null
+
+AM_CPPFLAGS+=-I$(top_srcdir)/src/network_inspectors/appid
+AM_DEFAULT_SOURCE_EXT = .cc
+
+check_PROGRAMS = \
+appid_simple_test \
+process_http_test
+
+TESTS = $(check_PROGRAMS)
+
+lib_list = \
+../../../network_inspectors/arp_spoof/libarp_spoof.a \
+../../../network_inspectors/packet_capture/libpacket_capture.a \
+../../../service_inspectors/back_orifice/libback_orifice.a \
+../../../service_inspectors/dce_rpc/libdce_rpc.a \
+../../../service_inspectors/dnp3/libdnp3.a \
+../../../service_inspectors/dns/libdns.a \
+../../../service_inspectors/ftp_telnet/libftp_telnet.a \
+../../../service_inspectors/gtp/libgtp_inspect.a \
+../../../service_inspectors/modbus/libmodbus.a \
+../../../service_inspectors/nhttp_inspect/libnhttp_inspect.a \
+../../../service_inspectors/rpc_decode/librpc_decode.a \
+../../../service_inspectors/sip/libsip.a \
+../../../service_inspectors/ssh/libssh.a \
+../../../service_inspectors/wizard/libwizard.a
+
+if STATIC_CODECS
+codec_list = \
+../../../codecs/root/libroot_codecs.a \
+../../../codecs/link/liblink_codecs.a
+endif
+
+
+test_list = ../../../catch/libcatch_tests.a
+
+if ENABLE_PIGLET
+pig_list = \
+piglet/libpiglet.a \
+piglet_plugins/libpiglet_plugins.a
+endif
+
+# order libs to avoid undefined symbols
+# from gnu linker
+snort_LIBS = \
+$(test_list) \
+../../../managers/libmanagers.a \
+../../../loggers/libloggers.a \
+../../../codecs/libcodecs.a \
+../../../codecs/ip/libip_codecs.a \
+../../../codecs/misc/libmisc_codecs.a \
+$(codec_list) \
+../../../network_inspectors/libnetwork_inspectors.a \
+../../../network_inspectors/binder/libbinder.a \
+../../../network_inspectors/normalize/libnormalize.a \
+../../../network_inspectors/perf_monitor/libperf_monitor.a \
+../../../network_inspectors/reputation/libreputation.a \
+../../../network_inspectors/appid/libappid.a \
+../../../network_inspectors/appid/client_plugins/libclient_plugins.a \
+../../../network_inspectors/appid/detector_plugins/libdetector_plugins.a \
+../../../network_inspectors/appid/service_plugins/libservice_plugins.a \
+../../../network_inspectors/appid/util/libappidutil.a \
+../../../service_inspectors/libservice_inspectors.a \
+$(lib_list) \
+../../../service_inspectors/imap/libimap.a \
+../../../service_inspectors/pop/libpop.a \
+../../../service_inspectors/smtp/libsmtp.a \
+../../../service_inspectors/ssl/libssl.a \
+../../../network_inspectors/port_scan/libport_scan.a \
+../../../stream/libstream.a \
+../../../stream/base/libstream_base.a \
+../../../stream/ip/libstream_ip.a \
+../../../stream/icmp/libstream_icmp.a \
+../../../stream/tcp/libstream_tcp.a \
+../../../stream/libtcp/libstream_libtcp.a \
+../../../stream/udp/libstream_udp.a \
+../../../stream/user/libstream_user.a \
+../../../stream/file/libstream_file.a \
+../../../stream/libstream_paf.a \
+../../../file_api/libfile_api.a \
+../../../mime/libmime.a \
+../../../service_inspectors/http_inspect/libhttp_inspect.a \
+$(pig_list) \
+../../../ips_options/libips_options.a \
+../../../search_engines/libsearch_engines.a \
+../../../target_based/libtarget_based.a \
+../../../main/libmain.a \
+../../../codecs/libcodec_module.a \
+../../../memory/libmemory.a \
+../../../host_tracker/libhost_tracker.a \
+../../../parser/libparser.a \
+../../../flow/libflow.a \
+../../../control/libcontrol.a \
+../../../filters/libfilter.a \
+../../../detection/libdetection.a \
+../../../framework/libframework.a \
+../../../time/libtime.a \
+../../../latency/liblatency.a \
+../../../profiler/libprofiler.a \
+../../../actions/libips_actions.a \
+../../../events/libevents.a \
+../../../hash/libhash.a \
+../../../log/liblog.a \
+../../../packet_io/libpacket_io.a \
+../../../helpers/libhelpers.a \
+../../../lua/liblua.a \
+../../../decompress/libdecompress.a \
+../../../sfip/libsfip.a \
+../../../sfrt/libsfrt.a \
+../../../protocols/libprotocols.a \
+../../../connectors/libconnectors.a \
+../../../connectors/file_connector/libfile_connector.a \
+../../../side_channel/libside_channel.a \
+../../../ports/libports.a \
+../../../utils/libutils.a
+
+appid_simple_test_CPPFLAGS = $(AM_CPPFLAGS) @CPPUTEST_CPPFLAGS@
+appid_simple_test_LDADD = ${snort_LIBS} @CPPUTEST_LDFLAGS@
+process_http_test_CPPFLAGS = $(AM_CPPFLAGS) @CPPUTEST_CPPFLAGS@
+process_http_test_LDADD = ${snort_LIBS} @CPPUTEST_LDFLAGS@
+
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2016-2016 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.
+//--------------------------------------------------------------------------
+
+// appid_simple_test.cc author stechew
+
+// Make some API calls to demonstrate that we can link with appid libs.
+
+#include <stdio.h>
+#include "util/fw_avltree.h"
+#include "protocols/protocol_ids.h"
+#include "sfip/sfip_t.h"
+#include "sfip/sf_ip.h"
+#include "fw_appid.h"
+
+int main()
+{
+ IpProtocol proto=IpProtocol::TCP;
+ sfip_t* ip = nullptr;
+ SFIP_RET status;
+
+ printf("Testing...\n");
+
+ ip = sfip_alloc("10.1.1.1", &status);
+ fwAvlInit();
+ appSharedDataAlloc(proto, ip);
+
+ return 0;
+}
+
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include <check.h>
+
+#include "parser/mstring.h"
+
+#include "appid_config.h"
+
+#include "external_apis.h"
+#include "fw_appid.h"
+#include "session_file.h"
+
+// FIXIT - this must go when snort2.9.x sf_ip.h changes are ported to snort++
+#include "sfaddr_temp.h"
+
+#if 1 // FIXIT-M hacks
+// not sure where this is defined; outside the appid tree probably
+using ControlDataSendFunc = void (*)();
+#endif
+
+extern void AppIdReload(struct _SnortConfig* sc, char* args, void** new_config);
+extern void* AppIdReloadSwap(struct _SnortConfig* sc, void* swap_config);
+extern void AppIdReloadFree(void* old_context);
+extern int AppIdReconfigure(uint16_t type, const uint8_t* data, uint32_t length,
+ void** new_context,
+ char* statusBuf, int statusBuf_len);
+extern int AppIdReconfigureSwap(uint16_t type, void* new_context, void** old_context);
+extern void AppIdReconfigureFree(uint16_t type, void* old_context, struct _THREAD_ELEMENT* te,
+ ControlDataSendFunc f);
+
+extern int processHTTPPacket(Packet* p, AppIdData* session, int direction,
+ HttpParsedHeaders* const headers, const AppIdConfig* pConfig);
+extern void appIdApiInit(struct AppIdApi*);
+extern void sfiph_build(Packet* p, const void* hdr, int family);
+extern void pickHttpXffAddress(Packet* p, AppIdData* appIdSession,
+ ThirdPartyAppIDAttributeData* attribute_data);
+
+AppIdApi appIdApi;
+
+// FIXIT: use APIs instead of using global
+extern AppIdData* pAppIdData;
+
+static char* testFilesPath = nullptr;
+static char rnaConfPath[PATH_MAX] = { 0 };
+
+static void testProcessHttpPacket(const char* useragent, const char* host, const char* referer,
+ const char* trailer)
+{
+ // FIXIT-M J these need to be cleared, probably
+ Packet p;
+ AppIdData session;
+
+ char buf[1024];
+ int bufLen;
+
+ session.common.flags = 0x311380;
+ session.hsession = (decltype(session.hsession))snort_calloc(sizeof(httpSession));
+ if (host)
+ {
+ session.hsession->host = snort_strdup(host);
+ strcpy(buf, "http://");
+ strcat(buf, host);
+ if (trailer)
+ strcat(buf, trailer);
+ else
+ strcat(buf, "/");
+ session.hsession->url = snort_strdup(buf);
+ }
+ if (useragent)
+ session.hsession->useragent = snort_strdup(useragent);
+ if (referer)
+ session.hsession->referer = snort_strdup(referer);
+ session.hsession->uri = snort_strdup("/");
+ session.hsession->cookie = snort_strdup(
+ "s_vi=[CS]v1|25B026B7851D124A-6000012D802520B2[CE]; CG=US:MD:Laurel; mbox=check#true#1336576860|session#1336576799559-724714#1336578660; SelectedEdition=www; rsi_segs_ttn=A09801_10001|A09801_10313; ug="
+ "4faa8b240a5fef0aa5147448c8005347; ugs=1; tnr:usrvtstg01=1336576805411%7C0%7C0%7C1%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C1%7Cf%7C"
+ "1%7C4%7C1336576805411; tnr:sesctmp01=1336576805411; s_cc=true; s_sq=%5B%5BB%5D%5D; adDEmas=R08&broadband&gblx.net&73&gbr&826027&0&10198&-&-&-&15275&; adDEon=true; s_ppv=13");
+ session.serviceAppId = APP_ID_NONE;
+ session.payloadAppId = APP_ID_NONE;
+ session.tpPayloadAppId = 1190;
+ session.scan_flags = 0x26;
+ session.ClientAppId = 0;
+
+ strcpy(buf, "GET / HTTP/1.1\r\n");
+ strcat(buf, "Host: ");
+ strcat(buf, host);
+ strcat(buf, "\r\nUser-Agent: ");
+ strcat(buf, useragent);
+ if (referer)
+ {
+ strcat(buf, "\r\nReferer: ");
+ strcat(buf, referer);
+ }
+ strcat(buf, "\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n");
+ strcat(buf,
+ "Accept-Language: en-us,en;q=0.5\r\nAccept-Encoding: gzip,deflate\r\nAccept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n");
+ strcat(buf, "Keep-Alive: 115\r\nConnection: keep-alive\r\n");
+ bufLen = strlen(buf);
+ strncat(buf,
+ "Cookie: s_vi=[CS]v1|25B026B7851D124A-6000012D802520B2[CE]; CG=US:MD:Laurel; mbox=check#true#1336576860|session#1336576799559-724714#1336578660; SelectedEdition=www; rsi_segs_ttn=A09801_10001|A09801_10313; ug=4faa8b240a5fef0aa5147448c8005347; ugs=1; tnr:usrvtstg01=1336576805411%7C0%7C0%7C1%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C1%7Cf%7C1%7C4%7C1336576805411; tnr:sesctmp01=1336576805411; s_cc=true; s_sq=%5B%5BB%5D%5D; adDEmas=R08&broadband&gblx.net&73&gbr&826027&0&10198&-&-&-&15275&; adDEon=true; s_ppv=13\r\n\r\n",
+ sizeof(buf) - bufLen - 1);
+
+ p.data = (decltype(p.data))snort_strdup(buf);
+ p.dsize = strlen((const char*)p.data);
+
+ processHTTPPacket(&p, &session, APP_ID_FROM_INITIATOR, nullptr, pAppidActiveConfig);
+
+ if (host)
+ {
+ snort_free(session.hsession->host);
+ snort_free(session.hsession->url);
+ }
+ if (referer)
+ snort_free(session.hsession->referer);
+ if (useragent)
+ snort_free(session.hsession->useragent);
+ snort_free(session.hsession->uri);
+ snort_free(session.hsession->cookie);
+ snort_free(session.hsession);
+
+ snort_free((uint8_t*)p.data);
+}
+
+void testFwAppIdSearch(const char* fileName)
+{
+ Packet pkt;
+ Flow flow;
+
+ FILE* file;
+ HttpParsedHeaders* httpHeader = nullptr;
+ AppId service;
+ bool isLoginSuccessful;
+ char* userName;
+ char* serviceVendor;
+ char* serviceVersion;
+ RNAServiceSubtype* serviceSubtype;
+ int moreData = 0;
+ char filePath[PATH_MAX];
+
+ strcpy(filePath, testFilesPath);
+ strcat(filePath, "/sessions/");
+ strcat(filePath, fileName);
+
+ file = fopen(filePath, "r");
+ assert(file != nullptr);
+
+ do
+ {
+ sessionFileReadSession(file, &flow);
+ moreData = sessionFileReadPacket(file, &pkt, &httpHeader);
+
+ pkt.flow = &flow;
+
+ sfiph_build(&pkt, &pkt.ip4h, pkt.family);
+
+ if (httpHeader)
+ {
+ httpHeaderCallback(&pkt, httpHeader);
+ }
+ else
+ {
+ fwAppIdSearch(&pkt);
+ }
+
+ if (pkt.data)
+ snort_free((uint8_t*)pkt.data);
+ memset(&pkt, 0, sizeof(pkt));
+
+ if (httpHeader)
+ {
+ if (httpHeader->host.start)
+ snort_free((uint8_t*)httpHeader->host.start);
+ if (httpHeader->url.start)
+ snort_free((uint8_t*)httpHeader->url.start);
+ if (httpHeader->method.start)
+ snort_free((uint8_t*)httpHeader->method.start);
+ if (httpHeader->userAgent.start)
+ snort_free((uint8_t*)httpHeader->userAgent.start);
+ if (httpHeader->referer.start)
+ snort_free((uint8_t*)httpHeader->referer.start);
+ if (httpHeader->via.start)
+ snort_free((uint8_t*)httpHeader->via.start);
+ if (httpHeader->responseCode.start)
+ snort_free((uint8_t*)httpHeader->responseCode.start);
+ if (httpHeader->server.start)
+ snort_free((uint8_t*)httpHeader->server.start);
+ if (httpHeader->xWorkingWith.start)
+ snort_free((uint8_t*)httpHeader->xWorkingWith.start);
+ if (httpHeader->contentType.start)
+ snort_free((uint8_t*)httpHeader->contentType.start);
+ snort_free(httpHeader);
+ httpHeader = nullptr;
+ }
+ }
+ while (moreData != -1);
+
+ LogMessage("==========================================================\n");
+ LogMessage("App name = %s\n", appGeAppName(appIdApi.geServiceAppId(pAppIdData)));
+ LogMessage("AppId = %d\n", appGeAppId(appGeAppName(appIdApi.geServiceAppId(pAppIdData))));
+ LogMessage("Service AppId = %d\n", appIdApi.geServiceAppId(pAppIdData));
+ LogMessage("Only Service AppId = %d\n", appIdApi.getOnlyServiceAppId(pAppIdData));
+ LogMessage("Misc AppId = %d\n", appIdApi.getMiscAppId(pAppIdData));
+ LogMessage("Client AppId = %d\n", appIdApi.getClientAppId(pAppIdData));
+ LogMessage("Payload AppId = %d\n", appIdApi.getPayloadAppId(pAppIdData));
+ LogMessage("Referred AppId = %d\n", appIdApi.getReferredAppId(pAppIdData));
+ LogMessage("Fw Service AppId = %d\n", appIdApi.getFwServiceAppId(pAppIdData));
+ LogMessage("Fw Misc AppId = %d\n", appIdApi.getFwMiscAppId(pAppIdData));
+ LogMessage("Fw Client AppId = %d\n", appIdApi.getFwClientAppId(pAppIdData));
+ LogMessage("Fw Payload AppId = %d\n", appIdApi.getFwPayloadAppId(pAppIdData));
+ LogMessage("Fw Referred AppId = %d\n", appIdApi.getFwReferredAppId(pAppIdData));
+ LogMessage("Is Session SSL Decrypted = %d\n", appIdApi.isSessionSslDecrypted(pAppIdData));
+ LogMessage("Is AppId Inspecting Session = %d\n", appIdApi.isAppIdInspectingSession(
+ pAppIdData));
+ LogMessage("Is AppId Available = %d\n", appIdApi.isAppIdAvailable(pAppIdData));
+ userName = appIdApi.getUserName(pAppIdData, &service, &isLoginSuccessful);
+ LogMessage("User name = %s, service = %d, isLoginSuccessful = %d\n",
+ userName, service, isLoginSuccessful);
+ LogMessage("Client version = %s\n", appIdApi.geClientVersion(pAppIdData));
+ // TODO: Is the flag argument correct?
+ LogMessage("Session attribute = %" PRIx64 "\n", appIdApi.getAppIdSessionAttribute(pAppIdData,
+ 0));
+ LogMessage("Flow type = %08X\n", appIdApi.getFlowType(pAppIdData));
+ appIdApi.geServiceInfo(pAppIdData, &serviceVendor, &serviceVersion, &serviceSubtype);
+ LogMessage("Service vendor = %s, version = %s\n",
+ serviceVendor, serviceVersion);
+ LogMessage("Service port = %d\n", appIdApi.geServicePort(pAppIdData));
+ LogMessage("Service IP = %s\n", inet_ntoa(appIdApi.geServiceIp(pAppIdData)));
+ LogMessage("HTTP user agent = %s\n", appIdApi.getHttpUserAgent(pAppIdData));
+ LogMessage("HTTP host = %s\n", appIdApi.getHttpHost(pAppIdData));
+ LogMessage("HTTP URL = %s\n", appIdApi.getHttpUrl(pAppIdData));
+ LogMessage("HTTP referer = %s\n", appIdApi.getHttpReferer(pAppIdData));
+ LogMessage("TLS host = %s\n", appIdApi.getTlsHost(pAppIdData));
+ LogMessage("NetBIOS name = %s\n", appIdApi.getNetbiosName(pAppIdData));
+
+ fclose(file);
+}
+
+START_TEST(ConfigParseTest)
+{
+ memset(&appidStaticConfig, 0, sizeof(appidStaticConfig));
+
+ appIdConfigParse(
+ "conf rna.conf, debug yes, dump_ports, memcap 0, app_stats_filename stats, app_stats_period 60, app_stats_rollover_size 100000, app_stats_rollover_time 60, app_detector_dir appid, instance_id 1, thirdparty_appid_dir thirdparty_appid");
+
+ ck_assert_str_eq(appidStaticConfig.conf_file, "rna.conf");
+ ck_assert_int_eq(appidStaticConfig.app_id_debug, 1);
+ ck_assert_int_eq(appidStaticConfig.app_id_dump_ports, 1);
+ ck_assert_uint_eq(appidStaticConfig.memcap, (32*1024*1024ULL));
+ ck_assert_str_eq(appidStaticConfig.app_stats_filename, "stats");
+ ck_assert_uint_eq(appidStaticConfig.app_stats_period, 60);
+ ck_assert_uint_eq(appidStaticConfig.app_stats_rollover_size, 100000);
+ ck_assert_uint_eq(appidStaticConfig.app_stats_rollover_time, 60);
+ ck_assert_str_eq(appidStaticConfig.app_id_detector_path, "appid");
+ ck_assert_uint_eq(appidStaticConfig.instance_id, 1);
+ ck_assert_str_eq(appidStaticConfig.appid_thirdparty_dir, "thirdparty_appid");
+}
+END_TEST START_TEST(InitFiniTest)
+{
+ memset(&appidStaticConfig, 0, sizeof(appidStaticConfig));
+
+ strcpy(appidStaticConfig.conf_file, rnaConfPath);
+ strcpy(appidStaticConfig.app_id_detector_path, testFilesPath);
+
+ AppIdCommonInit(&appidStaticConfig);
+ AppIdCommonFini();
+}
+
+END_TEST START_TEST(ReloadTest)
+{
+ AppIdConfig* pNewConfig = nullptr;
+ AppIdConfig* pOldConfig = nullptr;
+
+ memset(&appidStaticConfig, 0, sizeof(appidStaticConfig));
+
+ strcpy(appidStaticConfig.conf_file, rnaConfPath);
+ strcpy(appidStaticConfig.app_id_detector_path, testFilesPath);
+
+ AppIdCommonInit(&appidStaticConfig);
+
+ AppIdReload(nullptr, nullptr, (void**)&pNewConfig);
+ pOldConfig = AppIdReloadSwap(nullptr, pNewConfig);
+ AppIdReloadFree(pOldConfig);
+
+ AppIdCommonFini();
+}
+
+END_TEST START_TEST(ReconfigureTest)
+{
+ AppIdConfig* pNewConfig = nullptr;
+ AppIdConfig* pOldConfig = nullptr;
+
+ memset(&appidStaticConfig, 0, sizeof(appidStaticConfig));
+
+ strcpy(appidStaticConfig.conf_file, rnaConfPath);
+ strcpy(appidStaticConfig.app_id_detector_path, testFilesPath);
+
+ AppIdCommonInit(&appidStaticConfig);
+
+ AppIdReconfigure(0, nullptr, 0, (void**)&pNewConfig, nullptr, 0);
+ AppIdReconfigureSwap(0, pNewConfig, (void**)&pOldConfig);
+ AppIdReconfigureFree(0, pOldConfig, nullptr, nullptr);
+
+ AppIdCommonFini();
+}
+
+END_TEST START_TEST(HttpTest)
+{
+ memset(&appidStaticConfig, 0, sizeof(appidStaticConfig));
+
+ strcpy(appidStaticConfig.conf_file, rnaConfPath);
+ strcpy(appidStaticConfig.app_id_detector_path, testFilesPath);
+
+ AppIdCommonInit(&appidStaticConfig);
+
+ testProcessHttpPacket(
+ "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.7) Gecko/20100715 Ubuntu/9.04 (jaunty) Firefox/3.6.7",
+ "www.cnn.com",
+ nullptr, nullptr);
+ testProcessHttpPacket(
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/600.1.17 (KHTML, like Gecko) Version/7.1 Safari/537.85.10",
+ "www.cnn.com",
+ nullptr, nullptr);
+ testProcessHttpPacket(
+ "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)",
+ "www.cnn.com",
+ nullptr, nullptr);
+
+ AppIdCommonFini();
+}
+
+END_TEST START_TEST(HttpAfterReloadTest)
+{
+ AppIdConfig* pNewConfig = nullptr;
+ AppIdConfig* pOldConfig = nullptr;
+
+ memset(&appidStaticConfig, 0, sizeof(appidStaticConfig));
+
+ strcpy(appidStaticConfig.conf_file, rnaConfPath);
+ strcpy(appidStaticConfig.app_id_detector_path, testFilesPath);
+
+ AppIdCommonInit(&appidStaticConfig);
+
+ AppIdReload(nullptr, nullptr, (void**)&pNewConfig);
+ pOldConfig = AppIdReloadSwap(nullptr, pNewConfig);
+ AppIdReloadFree(pOldConfig);
+
+ testProcessHttpPacket(
+ "Mozilla/5.0 (Windows NT 6.0; rv:2.0) Gecko/20100101 Firefox/4.0 Opera 12.14",
+ "www.cnn.com",
+ nullptr, nullptr);
+ testProcessHttpPacket(
+ "Mozilla/5.0 (BlackBerry; U; BlackBerry 9900; en) AppleWebKit/534.11+ (KHTML, like Gecko) Version/7.1.0.346 Mobile Safari/534.11+",
+ "www.cnn.com",
+ nullptr, nullptr);
+ testProcessHttpPacket(
+ "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36",
+ "www.cnn.com",
+ nullptr, nullptr);
+
+ AppIdCommonFini();
+}
+
+END_TEST START_TEST(HttpAfterReconfigureTest)
+{
+ AppIdConfig* pNewConfig = nullptr;
+ AppIdConfig* pOldConfig = nullptr;
+
+ memset(&appidStaticConfig, 0, sizeof(appidStaticConfig));
+
+ strcpy(appidStaticConfig.conf_file, rnaConfPath);
+ strcpy(appidStaticConfig.app_id_detector_path, testFilesPath);
+
+ AppIdCommonInit(&appidStaticConfig);
+
+ AppIdReconfigure(0, nullptr, 0, (void**)&pNewConfig, nullptr, 0);
+ AppIdReconfigureSwap(0, pNewConfig, (void**)&pOldConfig);
+ AppIdReconfigureFree(0, pOldConfig, nullptr, nullptr);
+
+ testProcessHttpPacket("Wget/1.9.1",
+ "www.cnn.com",
+ nullptr, nullptr);
+ testProcessHttpPacket(
+ "Mozilla/5.0 (Linux; U; Android 4.0.3; ko-kr; LG-L160L Build/IML74K) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
+ "www.cnn.com",
+ nullptr, nullptr);
+
+ AppIdCommonFini();
+}
+
+END_TEST START_TEST(HttpAfterReloadReconfigureTest)
+{
+ AppIdConfig* pNewConfig = nullptr;
+ AppIdConfig* pOldConfig = nullptr;
+
+ memset(&appidStaticConfig, 0, sizeof(appidStaticConfig));
+
+ strcpy(appidStaticConfig.conf_file, rnaConfPath);
+ strcpy(appidStaticConfig.app_id_detector_path, testFilesPath);
+
+ AppIdCommonInit(&appidStaticConfig);
+
+ AppIdReload(nullptr, nullptr, (void**)&pNewConfig);
+ pOldConfig = AppIdReloadSwap(nullptr, pNewConfig);
+ AppIdReloadFree(pOldConfig);
+
+ pNewConfig = nullptr;
+ pOldConfig = nullptr;
+
+ AppIdReconfigure(0, nullptr, 0, (void**)&pNewConfig, nullptr, 0);
+ AppIdReconfigureSwap(0, pNewConfig, (void**)&pOldConfig);
+ AppIdReconfigureFree(0, pOldConfig, nullptr, nullptr);
+
+ testProcessHttpPacket(
+ "Mozilla/5.0 (Linux; U; Android 4.0.3; ko-kr; LG-L160L Build/IML74K) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
+ "www.123.com",
+ "http://www.cnn.com", nullptr);
+ testProcessHttpPacket(
+ "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.7) Gecko/20100715 Ubuntu/9.04 (jaunty) Firefox/3.6.7",
+ "www.cnn.com",
+ nullptr, "/tech?a=1&b=2");
+
+ AppIdCommonFini();
+}
+
+END_TEST START_TEST(HttpXffTest)
+{
+ Packet p = { 0 };
+ AppIdData session = { 0 };
+ httpSession hsession = { 0 };
+ ThirdPartyAppIDAttributeData tpData = { 0 };
+ SFIP_RET status;
+ sfaddr_t* xffAddr = sfaddr_alloc("1.1.1.1", &status);
+
+ // Only X-Forwarded-For
+ session.hsession = &hsession;
+ tpData.numXffFields = 1;
+ tpData.xffFieldValue[0].field = HTTP_XFF_FIELD_X_FORWARDED_FOR;
+ tpData.xffFieldValue[0].value = "1.1.1.1";
+ pickHttpXffAddress(&p, &session, &tpData);
+ ck_assert_int_eq(sfip_compare(session.hsession->xffAddr, xffAddr), SFIP_EQUAL);
+ sfaddr_free(session.hsession->xffAddr);
+
+ // Only True-Client-IP
+ memset(&p, 0, sizeof(p));
+ memset(&session, 0, sizeof(session));
+ memset(&hsession, 0, sizeof(hsession));
+ memset(&tpData, 0, sizeof(tpData));
+ session.hsession = &hsession;
+ tpData.numXffFields = 1;
+ tpData.xffFieldValue[0].field = HTTP_XFF_FIELD_TRUE_CLIENT_IP;
+ tpData.xffFieldValue[0].value = "1.1.1.1";
+ pickHttpXffAddress(&p, &session, &tpData);
+ ck_assert_int_eq(sfip_compare(session.hsession->xffAddr, xffAddr), SFIP_EQUAL);
+ sfaddr_free(session.hsession->xffAddr);
+
+ // X-Forwarded-For and True-Client-IP
+ memset(&p, 0, sizeof(p));
+ memset(&session, 0, sizeof(session));
+ memset(&hsession, 0, sizeof(hsession));
+ memset(&tpData, 0, sizeof(tpData));
+ session.hsession = &hsession;
+ tpData.numXffFields = 2;
+ tpData.xffFieldValue[0].field = HTTP_XFF_FIELD_TRUE_CLIENT_IP;
+ tpData.xffFieldValue[0].value = "2.2.2.2";
+ tpData.xffFieldValue[1].field = HTTP_XFF_FIELD_X_FORWARDED_FOR;
+ tpData.xffFieldValue[1].value = "1.1.1.1";
+ pickHttpXffAddress(&p, &session, &tpData);
+ ck_assert_int_eq(sfip_compare(session.hsession->xffAddr, xffAddr), SFIP_EQUAL);
+ sfaddr_free(session.hsession->xffAddr);
+
+ // Comma-separated list in X-Forwarded-For
+ memset(&p, 0, sizeof(p));
+ memset(&session, 0, sizeof(session));
+ memset(&hsession, 0, sizeof(hsession));
+ memset(&tpData, 0, sizeof(tpData));
+ session.hsession = &hsession;
+ tpData.numXffFields = 1;
+ tpData.xffFieldValue[0].field = HTTP_XFF_FIELD_X_FORWARDED_FOR;
+ tpData.xffFieldValue[0].value = snort_strdup("1.1.1.1, 2.2.2.2");
+ pickHttpXffAddress(&p, &session, &tpData);
+ ck_assert_int_eq(sfip_compare(session.hsession->xffAddr, xffAddr), SFIP_EQUAL);
+ snort_free(tpData.xffFieldValue[0].value);
+ sfaddr_free(session.hsession->xffAddr);
+
+ // Custom XFF
+ static char* defaultXffPrecedence[] = { "Custom-XFF", HTTP_XFF_FIELD_X_FORWARDED_FOR,
+ HTTP_XFF_FIELD_TRUE_CLIENT_IP };
+ memset(&p, 0, sizeof(p));
+ memset(&session, 0, sizeof(session));
+ memset(&hsession, 0, sizeof(hsession));
+ memset(&tpData, 0, sizeof(tpData));
+ session.hsession = &hsession;
+ session.hsession->xffPrecedence = defaultXffPrecedence;
+ session.hsession->numXffFields = 3;
+ tpData.numXffFields = 2;
+ tpData.xffFieldValue[0].field = HTTP_XFF_FIELD_X_FORWARDED_FOR;
+ tpData.xffFieldValue[0].value = "2.2.2.2";
+ tpData.xffFieldValue[1].field = "Custom-XFF";
+ tpData.xffFieldValue[1].value = "1.1.1.1";
+ pickHttpXffAddress(&p, &session, &tpData);
+ ck_assert_int_eq(sfip_compare(session.hsession->xffAddr, xffAddr), SFIP_EQUAL);
+ sfaddr_free(session.hsession->xffAddr);
+
+ sfaddr_free(xffAddr);
+
+ snort_free((uint8_t*)p.data);
+}
+
+END_TEST START_TEST(AimSessionTest)
+{
+ testFwAppIdSearch("aim.ssn");
+
+ ck_assert_str_eq(appGeAppName(appIdApi.geServiceAppId(pAppIdData)), "AOL Instant Messenger");
+ ck_assert_uint_eq(appIdApi.geServiceAppId(pAppIdData), APP_ID_AOL_INSTANT_MESSENGER);
+ ck_assert_uint_eq(appIdApi.getClientAppId(pAppIdData), APP_ID_AOL_INSTANT_MESSENGER);
+
+ appSharedDataDelete(pAppIdData);
+ pAppIdData = nullptr;
+}
+
+END_TEST START_TEST(CnnSessionTest)
+{
+ testFwAppIdSearch("cnn.ssn");
+
+ ck_assert_str_eq(appGeAppName(appIdApi.geServiceAppId(pAppIdData)), "HTTP");
+ ck_assert_uint_eq(appIdApi.geServiceAppId(pAppIdData), APP_ID_HTTP);
+ ck_assert_uint_eq(appIdApi.getClientAppId(pAppIdData), APP_ID_FIREFOX);
+ ck_assert_uint_eq(appIdApi.getPayloadAppId(pAppIdData), 1190); // CNN app
+ ck_assert_str_eq(appIdApi.getHttpHost(pAppIdData), "www.cnn.com");
+
+ appSharedDataDelete(pAppIdData);
+ pAppIdData = nullptr;
+}
+
+END_TEST START_TEST(DnsSessionTest)
+{
+ testFwAppIdSearch("dns.ssn");
+
+ ck_assert_str_eq(appGeAppName(appIdApi.geServiceAppId(pAppIdData)), "DNS");
+ ck_assert_uint_eq(appIdApi.geServiceAppId(pAppIdData), APP_ID_DNS);
+ ck_assert_uint_eq(appIdApi.getClientAppId(pAppIdData), APP_ID_DNS);
+ ck_assert_uint_eq(appIdApi.geServicePort(pAppIdData), 53);
+
+ appSharedDataDelete(pAppIdData);
+ pAppIdData = nullptr;
+}
+
+END_TEST START_TEST(ImapSessionTest)
+{
+ testFwAppIdSearch("imap.ssn");
+
+ // TODO: Investigate why IMAP appids are not showing up
+
+ appSharedDataDelete(pAppIdData);
+ pAppIdData = nullptr;
+}
+
+END_TEST START_TEST(MdnsSessionTest)
+{
+ testFwAppIdSearch("mdns.ssn");
+
+ ck_assert_str_eq(appGeAppName(appIdApi.geServiceAppId(pAppIdData)), "MDNS");
+ ck_assert_uint_eq(appIdApi.geServiceAppId(pAppIdData), APP_ID_MDNS);
+ ck_assert_uint_eq(appIdApi.geServicePort(pAppIdData), 5353);
+
+ appSharedDataDelete(pAppIdData);
+ pAppIdData = nullptr;
+}
+
+END_TEST START_TEST(MsnSessionTest)
+{
+ testFwAppIdSearch("msn.ssn");
+
+ ck_assert_str_eq(appGeAppName(appIdApi.geServiceAppId(pAppIdData)), "MSN Messenger");
+ ck_assert_uint_eq(appIdApi.geServiceAppId(pAppIdData), APP_ID_MSN_MESSENGER);
+
+ appSharedDataDelete(pAppIdData);
+ pAppIdData = nullptr;
+}
+
+END_TEST START_TEST(NetbiosNsSessionTest)
+{
+ testFwAppIdSearch("netbios_ns.ssn");
+
+ // TODO: Investigate why NetBIOS name service appids are not showing up
+
+ appSharedDataDelete(pAppIdData);
+ pAppIdData = nullptr;
+}
+
+END_TEST START_TEST(NetbiosSsSessionTest)
+{
+ testFwAppIdSearch("netbios_ss.ssn");
+
+ // TODO: Investigate why NetBIOS ss appids are not showing up
+
+ appSharedDataDelete(pAppIdData);
+ pAppIdData = nullptr;
+}
+
+END_TEST START_TEST(PatternSessionTest)
+{
+ testFwAppIdSearch("pattern.ssn");
+
+ ck_assert_str_eq(appGeAppName(appIdApi.geServiceAppId(pAppIdData)), "3Com AMP3");
+ ck_assert_uint_eq(appIdApi.geServiceAppId(pAppIdData), 3000);
+ ck_assert_uint_eq(appIdApi.getClientAppId(pAppIdData), 3000);
+
+ appSharedDataDelete(pAppIdData);
+ pAppIdData = nullptr;
+}
+
+END_TEST START_TEST(Pop3SessionTest)
+{
+ testFwAppIdSearch("pop3.ssn");
+
+ ck_assert_str_eq(appGeAppName(appIdApi.geServiceAppId(pAppIdData)), "POP3");
+ ck_assert_uint_eq(appIdApi.geServiceAppId(pAppIdData), APP_ID_POP3);
+ ck_assert_uint_eq(appIdApi.geServicePort(pAppIdData), 110);
+
+ appSharedDataDelete(pAppIdData);
+ pAppIdData = nullptr;
+}
+
+END_TEST START_TEST(RfbSessionTest)
+{
+ testFwAppIdSearch("rfb.ssn");
+
+ ck_assert_str_eq(appGeAppName(appIdApi.geServiceAppId(pAppIdData)), "RFB");
+ ck_assert_uint_eq(appIdApi.geServiceAppId(pAppIdData), APP_ID_VNC_RFB);
+ ck_assert_uint_eq(appIdApi.getClientAppId(pAppIdData), APP_ID_VNC);
+ ck_assert_uint_eq(appIdApi.geServicePort(pAppIdData), 5900);
+
+ appSharedDataDelete(pAppIdData);
+ pAppIdData = nullptr;
+}
+
+END_TEST START_TEST(RtpSessionTest)
+{
+ testFwAppIdSearch("rtp.ssn");
+
+ // TODO: Investigate why RTP appids are not showing up
+
+ appSharedDataDelete(pAppIdData);
+ pAppIdData = nullptr;
+}
+
+END_TEST START_TEST(SmtpSessionTest)
+{
+ testFwAppIdSearch("smtp.ssn");
+
+ ck_assert_str_eq(appGeAppName(appIdApi.geServiceAppId(pAppIdData)), "SMTP");
+ ck_assert_uint_eq(appIdApi.geServiceAppId(pAppIdData), APP_ID_SMTP);
+ ck_assert_uint_eq(appIdApi.geServicePort(pAppIdData), 25);
+
+ appSharedDataDelete(pAppIdData);
+ pAppIdData = nullptr;
+}
+
+END_TEST START_TEST(TimbuktuSessionTest)
+{
+ testFwAppIdSearch("timbuktu.ssn");
+
+ ck_assert_str_eq(appGeAppName(appIdApi.geServiceAppId(pAppIdData)), "Timbuktu");
+ ck_assert_uint_eq(appIdApi.geServiceAppId(pAppIdData), APP_ID_TIMBUKTU);
+ ck_assert_uint_eq(appIdApi.geServiceAppId(pAppIdData), APP_ID_TIMBUKTU);
+ ck_assert_uint_eq(appIdApi.geServicePort(pAppIdData), 407);
+
+ appSharedDataDelete(pAppIdData);
+ pAppIdData = nullptr;
+}
+
+END_TEST START_TEST(WebexSessionTest)
+{
+ testFwAppIdSearch("webex.ssn");
+
+ ck_assert_str_eq(appGeAppName(appIdApi.geServiceAppId(pAppIdData)), "HTTP");
+ ck_assert_uint_eq(appIdApi.geServiceAppId(pAppIdData), APP_ID_HTTP);
+ ck_assert_uint_eq(appIdApi.getClientAppId(pAppIdData), 2932); // WebEx
+ ck_assert_uint_eq(appIdApi.getPayloadAppId(pAppIdData), 2932); // WebEx
+
+ appSharedDataDelete(pAppIdData);
+ pAppIdData = nullptr;
+}
+
+END_TEST START_TEST(YmSessionTest)
+{
+ testFwAppIdSearch("ym.ssn");
+
+ // TODO: Investigate why Yahoo messenger appids are not showing up
+
+ appSharedDataDelete(pAppIdData);
+ pAppIdData = nullptr;
+}
+
+END_TEST
+
+static void appIdTestSetup(void)
+{
+ static SessionAPI sessionAPI = { 0 };
+ static StreamAPI streamAPI = { 0 };
+
+ testFilesPath = getenv("APPID_TESTS_PATH");
+
+ if (testFilesPath == nullptr)
+ {
+ printf("Env variable APPID_TESTS_PATH is not set. Exiting ...\n");
+ exit(-1);
+ }
+
+ strcpy(rnaConfPath, testFilesPath);
+ strcat(rnaConfPath, "/rna.conf");
+
+ _dpd.tokenSplit = mSplit;
+ _dpd.tokenFree = mSplitFree;
+ LogMessage = logMsg;
+ _dpd.errMsg = errMsg;
+ _dpd.debugMsg = debugMsg;
+ _dpd.addProtocolReference = addProtocolReference;
+ _dpd.addPreproc = addPreproc;
+ _dpd.getParserPolicy = getParserPolicy;
+ _dpd.getDefaultPolicy = getDefaultPolicy;
+ _dpd.isAppIdRequired = isAppIdRequired;
+ _dpd.getSnortInstance = getSnortInstance;
+ _dpd.findProtocolReference = findProtocolReference;
+
+ sessionAPI.enable_preproc_all_ports = enable_preproc_all_ports;
+ sessionAPI.get_application_data = get_application_data;
+ sessionAPI.set_application_data = set_application_data;
+ sessionAPI.get_packet_direction = get_packet_direction;
+ sessionAPI.get_session_flags = get_session_flags;
+ sessionAPI.get_session_ip_address = get_session_ip_address;
+ sessionAPI.get_application_protocol_id = get_application_protocol_id;
+ sessionAPI.get_http_xff_precedence = get_http_xff_precedence;
+ _dpd.sessionAPI = &sessionAPI;
+
+ streamAPI.is_session_decrypted = is_session_decrypted;
+ streamAPI.set_application_id = set_application_id;
+ streamAPI.is_session_http2 = is_session_http2;
+ _dpd.streamAPI = &streamAPI;
+
+ _dpd.searchAPI = &searchAPI;
+
+ appIdApiInit(&appIdApi);
+}
+
+static void sessionTcaseSetup(void)
+{
+ memset(&appidStaticConfig, 0, sizeof(appidStaticConfig));
+
+ strcpy(appidStaticConfig.conf_file, rnaConfPath);
+ strcpy(appidStaticConfig.app_id_detector_path, testFilesPath);
+
+ AppIdCommonInit(&appidStaticConfig);
+}
+
+static void sessionTcaseClean(void)
+{
+ AppIdCommonFini();
+}
+
+static Suite* setupAppIdSuite(void)
+{
+ Suite* appIdSuite;
+ TCase* frameworkTcase;
+ TCase* httpTcase;
+ TCase* sessionTcase;
+
+ appIdSuite = suite_create("AppId");
+
+ // Create Framework test case
+ frameworkTcase = tcase_create("FrameworkTestCase");
+ tcase_add_checked_fixture(frameworkTcase, nullptr, nullptr);
+
+ // Add tests to Framework test case
+ tcase_add_test(frameworkTcase, ConfigParseTest);
+ tcase_add_test(frameworkTcase, InitFiniTest);
+ tcase_add_test(frameworkTcase, ReloadTest);
+ tcase_add_test(frameworkTcase, ReconfigureTest);
+
+ suite_add_tcase(appIdSuite, frameworkTcase);
+
+ // Create Http test case
+ httpTcase = tcase_create("HttpTestCase");
+ tcase_add_checked_fixture(httpTcase, nullptr, nullptr);
+
+ // Add tests to Http test case
+ tcase_add_test(httpTcase, HttpTest);
+ tcase_add_test(httpTcase, HttpAfterReloadTest);
+ tcase_add_test(httpTcase, HttpAfterReconfigureTest);
+ tcase_add_test(httpTcase, HttpAfterReloadReconfigureTest);
+ tcase_add_test(httpTcase, HttpXffTest);
+
+ suite_add_tcase(appIdSuite, httpTcase);
+
+ // Create Session test case
+ sessionTcase = tcase_create("SessionTestCase");
+ tcase_add_checked_fixture(sessionTcase, nullptr, nullptr);
+
+ // Add tests to Session test case
+ tcase_add_test(sessionTcase, AimSessionTest);
+ tcase_add_test(sessionTcase, CnnSessionTest);
+ tcase_add_test(sessionTcase, DnsSessionTest);
+ tcase_add_test(sessionTcase, ImapSessionTest);
+ tcase_add_test(sessionTcase, MdnsSessionTest);
+ tcase_add_test(sessionTcase, MsnSessionTest);
+ tcase_add_test(sessionTcase, NetbiosNsSessionTest);
+ tcase_add_test(sessionTcase, NetbiosSsSessionTest);
+ tcase_add_test(sessionTcase, PatternSessionTest);
+ tcase_add_test(sessionTcase, Pop3SessionTest);
+ tcase_add_test(sessionTcase, RfbSessionTest);
+ tcase_add_test(sessionTcase, RtpSessionTest);
+ tcase_add_test(sessionTcase, SmtpSessionTest);
+ tcase_add_test(sessionTcase, TimbuktuSessionTest);
+ tcase_add_test(sessionTcase, WebexSessionTest);
+ tcase_add_test(sessionTcase, YmSessionTest);
+
+ suite_add_tcase(appIdSuite, sessionTcase);
+
+ return appIdSuite;
+}
+
+int main(int argc, char* argv[])
+{
+ int opt, debug = 0;
+ int numberFailed;
+ Suite* appIdSuite;
+ SRunner* appIdRunner;
+
+ while ((opt = getopt(argc, argv, "dh")) != -1)
+ {
+ switch (opt)
+ {
+ case 'd':
+ debug = 1;
+ break;
+ case 'h':
+ printf(
+ "Usage:\n\
+ -d: Run test in no fork mode for debugging in gdb.\n\
+ -h: This text.\n");
+ return EXIT_SUCCESS;
+ }
+ }
+
+ appIdTestSetup();
+
+ // Create a test runner for AppId suite
+ appIdSuite = setupAppIdSuite();
+ appIdRunner = srunner_create(appIdSuite);
+
+ if (debug)
+ {
+ srunner_set_fork_status(appIdRunner, CK_NOFORK);
+ }
+
+ // Set test results format to TAP and specify file name
+ srunner_set_tap(appIdRunner, "AppIdTests.tap~");
+
+ // Run test cases in AppId suite
+ srunner_run(appIdRunner, nullptr, "FrameworkTestCase", CK_NORMAL);
+
+ system("cp AppIdTests.tap~ AppIdTests.tap");
+
+ srunner_run(appIdRunner, nullptr, "HttpTestCase", CK_NORMAL);
+
+ system("cat AppIdTests.tap~ >> AppIdTests.tap");
+
+ sessionTcaseSetup();
+ srunner_run(appIdRunner, nullptr, "SessionTestCase", CK_NORMAL);
+ sessionTcaseClean();
+
+ system("cat AppIdTests.tap~ >> AppIdTests.tap");
+
+ numberFailed = srunner_ntests_failed(appIdRunner);
+ srunner_free(appIdRunner);
+ return (numberFailed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
--- /dev/null
+//#include <stdarg.h>
+#include "external_apis.h"
+#include "session_file.h"
+#include "appid_flow_data.h"
+
+#include "protocols/packet.h"
+#include "protocols/tcp.h"
+#include "protocols/udp.h"
+
+AppIdData* pAppIdData = nullptr;
+
+/***********************************************************
+ * Local functions
+ **********************************************************/
+static void determinePacketDirection(Packet* p, uint16_t p_port, uint16_t scb_port, int is_sport)
+{
+ if (is_sport)
+ p->packet_flags |= (p_port == scb_port) ? PKT_FROM_CLIENT : PKT_FROM_SERVER;
+ else
+ p->packet_flags |= (p_port == scb_port) ? PKT_FROM_SERVER : PKT_FROM_CLIENT;
+}
+
+static void setPacketDirectionFlag(Packet* p, SessionControlBlock* session)
+{
+ if (p->is_ip4())
+ {
+ if (sfip_fast_eq4(p->ptrs.ip_api.get_src(), &session->client_ip))
+ {
+ if (p->is_tcp())
+ determinePacketDirection(p, p->ptrs.tcph->src_port(), session->client_port, true);
+ else if (p->is_udp())
+ determinePacketDirection(p, p->ptrs.udph->src_port(), session->client_port, true);
+ else
+ p->packet_flags |= PKT_FROM_CLIENT;
+ }
+ else if (sfip_fast_eq4(p->ptrs.ip_api.get_dst(), &session->client_ip))
+ {
+ if (p->is_tcp())
+ determinePacketDirection(p, p->ptrs.tcph->dst_port(), session->client_port, false);
+ else if (p->is_udp())
+ determinePacketDirection(p, p->ptrs.udph->dst_port(), session->client_port, false);
+ else
+ p->packet_flags |= PKT_FROM_SERVER;
+ }
+ }
+ else
+ {
+ if (sfip_fast_eq6(p->ptrs.ip_api.get_src(), &session->client_ip))
+ {
+ if (p->is_tcp())
+ determinePacketDirection(p, p->ptrs.tcph->src_port(), session->client_port, true);
+ else if (p->is_udp())
+ determinePacketDirection(p, p->ptrs.udph->src_port(), session->client_port, true);
+ else
+ p->packet_flags |= PKT_FROM_CLIENT;
+ }
+ else if (sfip_fast_eq6(p->ptrs.ip_api.get_dst(), &session->client_ip))
+ {
+ if (p->is_tcp())
+ determinePacketDirection(p, p->ptrs.tcph->dst_port(), session->client_port, false);
+ else if (p->is_udp())
+ determinePacketDirection(p, p->ptrs.udph->dst_port(), session->client_port, false);
+ else
+ p->packet_flags |= PKT_FROM_SERVER;
+ }
+ }
+}
+
+NORETURN void FatalError(const char* format,...)
+{
+ va_list arg;
+
+ printf("FATAL ERROR: ");
+
+ va_start (arg, format);
+ vfprintf (stdout, format, arg);
+ va_end (arg);
+ fflush(stdout);
+
+ exit(-1);
+}
+
+void LogMessage(const char* format,...)
+{
+ va_list arg;
+
+ printf("LOG MESSAGE: ");
+
+ va_start (arg, format);
+ vfprintf (stdout, format, arg);
+ va_end (arg);
+ fflush(stdout);
+}
+
+/***********************************************************
+ * _dpd APIs
+ **********************************************************/
+void logMsg(const char* format, ...)
+{
+ va_list arg;
+
+ printf("LOG: ");
+
+ va_start (arg, format);
+ vfprintf (stdout, format, arg);
+ va_end (arg);
+ fflush(stdout);
+}
+
+void errMsg(const char* format, ...)
+{
+ va_list arg;
+
+ printf("ERROR: ");
+
+ va_start (arg, format);
+ vfprintf (stdout, format, arg);
+ va_end (arg);
+ fflush(stdout);
+}
+
+void debugMsg(uint64_t, const char* format, ...)
+{
+ va_list arg;
+
+ printf("DEBUG: ");
+
+ va_start (arg, format);
+ vfprintf (stdout, format, arg);
+ va_end (arg);
+ fflush(stdout);
+}
+
+int16_t addProtocolReference(const char*)
+{
+ return 0;
+}
+
+void* addPreproc(struct _SnortConfig*, void (*)(void*, void*), uint16_t, uint32_t, uint32_t)
+{
+ return nullptr;
+}
+
+tSfPolicyId getParserPolicy(struct _SnortConfig*)
+{
+ return 0;
+}
+
+tSfPolicyId getDefaultPolicy(void)
+{
+ return 1;
+}
+
+bool isAppIdRequired(void)
+{
+ return false;
+}
+
+uint32_t getSnortInstance(void)
+{
+ return 0;
+}
+
+int16_t findProtocolReference(const char*)
+{
+ return 0;
+}
+
+/***********************************************************
+ * Session APIs
+ **********************************************************/
+void enable_preproc_all_ports(struct _SnortConfig*, uint32_t, uint32_t)
+{
+}
+
+void* get_application_data(void*, uint32_t)
+{
+ return pAppIdData;
+}
+
+int set_application_data(void*, uint32_t, AppIdData* data, StreamAppDataFree)
+{
+ pAppIdData = data;
+
+ return 0;
+}
+
+uint32_t get_packet_direction(Packet* p)
+{
+ if ((p == nullptr) || (p->flow == nullptr))
+ return 0;
+
+ setPacketDirectionFlag(p, p->flow);
+
+ return (p->packet_flags & (PKT_FROM_SERVER | PKT_FROM_CLIENT));
+}
+
+uint32_t get_session_flags(void* ssnptr)
+{
+ SessionControlBlock* scb = (SessionControlBlock*)ssnptr;
+ return scb->ha_state.session_flags;
+}
+
+sfaddr_t* get_session_ip_address(void* scbptr, uint32_t direction)
+{
+ SessionControlBlock* scb = (SessionControlBlock*)scbptr;
+
+ if (scb != nullptr)
+ {
+ switch (direction)
+ {
+ case SSN_DIR_FROM_SERVER:
+ return (sfaddr_t*)(&(scb)->server_ip);
+
+ case SSN_DIR_FROM_CLIENT:
+ return (sfaddr_t*)(&(scb)->client_ip);
+
+ default:
+ break;
+ }
+ }
+
+ return nullptr;
+}
+
+bool is_session_decrypted(void*)
+{
+ return true;
+}
+
+void set_application_id(void*, int16_t, int16_t, int16_t, int16_t)
+{
+}
+
+bool is_session_http2(void*)
+{
+ return false;
+}
+
+int16_t get_application_protocol_id(void*)
+{
+ return 0;
+}
+
+char** get_http_xff_precedence(void*, uint32_t, int*)
+{
+ return nullptr;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// external_apis.h author Sourcefire Inc.
+
+#ifndef EXTERNAL_APIS_H
+#define EXTERNAL_APIS_H
+
+#include <cstdint>
+
+#include "flow/flow.h"
+
+struct sfaddr_t;
+struct SnortConfig;
+struct Packet;
+using tSfPolicyId = int;
+
+#define PKT_FROM_SERVER 0x00000040 /* this packet came from the server
+ side of a connection (TCP) */
+#define PKT_FROM_CLIENT 0x00000080 /* this packet came from the client
+ side of a connection (TCP) */
+// _dpd APIs
+void logMsg(const char*, ...);
+void errMsg(const char*, ...);
+void debugMsg(uint64_t type, const char*, ...);
+int16_t addProtocolReference(const char* protocol);
+
+void* addPreproc(
+ SnortConfig*, void (* pp_func)(void*, void*), uint16_t priority,
+ uint32_t appId, uint32_t flags);
+
+tSfPolicyId getParserPolicy(SnortConfig*);
+tSfPolicyId getDefaultPolicy();
+bool isAppIdRequired();
+uint32_t getSnortInstance();
+int16_t findProtocolReference(const char* app);
+
+// Session APIs
+void enable_preproc_all_ports(SnortConfig*, uint32_t appId, uint32_t flags);
+void* get_application_data(void* stream_session, uint32_t protocol);
+int set_application_data(void* scbptr, uint32_t protocol, void* data, StreamAppDataFree);
+uint32_t get_packet_direction(Packet*);
+uint32_t get_session_flags(void* ssnptr);
+sfaddr_t* get_session_ip_address(void* scbptr, uint32_t direction);
+int16_t get_application_protocol_id(void* scbptr);
+char** get_http_xff_precedence(void* ssn, uint32_t flags, int* nFields);
+
+// Stream APIs
+bool is_session_decrypted(void* stream_session);
+void set_application_id(
+ void* ssnptr, int16_t serviceAppid, int16_t ClientAppid,
+ int16_t payloadAppId, int16_t miscAppid);
+
+bool is_session_http2(void* ssn);
+
+#endif
--- /dev/null
+/*
+* $Id: mpse.c,v 1.2 2015/03/25 14:45:18 andrbake Exp $
+*
+* mpse.c
+*
+* An abstracted interface to the Multi-Pattern Matching routines,
+* thats why we're passing 'void *' objects around.
+*
+* Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
+* Copyright (C) 2002-2013 Sourcefire, Inc.
+* Marc A Norton <mnorton@sourcefire.com>
+*
+* 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.
+**
+*/
+#include <assert.h>
+
+#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"
+#ifndef DYNAMIC_PREPROC_CONTEXT
+#include "snort.h"
+#endif
+#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*)snort_calloc(sizeof(MPSE) );
+ p->method = method;
+ p->verbose = 0;
+ p->obj = nullptr;
+ 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 )
+ {
+ snort_free(p);
+ p = nullptr;
+ }
+
+ 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*)snort_calloc(sizeof(MPSE) );
+ p->method = method;
+ p->verbose = 0;
+ p->obj = nullptr;
+ 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 )
+ {
+ snort_free(p);
+ p = nullptr;
+ }
+
+ 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 == nullptr)
+ 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 == nullptr)
+ return;
+
+ switch ( p->method )
+ {
+ case MPSE_AC_BNFA:
+ case MPSE_AC_BNFA_Q:
+ if (p->obj)
+ bnfaFree((bnfa_struct_t*)p->obj);
+ snort_free(p);
+ return;
+
+ case MPSE_AC:
+ if (p->obj)
+ acsmFree((ACSM_STRUCT*)p->obj);
+ snort_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);
+ snort_free(p);
+ return;
+
+ case MPSE_LOWMEM:
+ case MPSE_LOWMEM_Q:
+ if (p->obj)
+ KTrieDelete((KTRIE_STRUCT*)p->obj);
+ snort_free(p);
+ return;
+
+#ifdef INTEL_SOFT_CPM
+ case MPSE_INTEL_CPM:
+ if (p->obj)
+ IntelPmDelete((IntelPm*)p->obj);
+ snort_free(p);
+ break;
+#endif
+
+ default:
+ snort_free(p);
+ assert(false);
+ 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 == nullptr)
+ 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();
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2016-2016 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.
+//--------------------------------------------------------------------------
+
+#include "appid_api.h"
+#include "fw_appid.h"
+
+extern void appIdApiInit(struct AppIdApi*);
+
+AppIdApi appIdApi;
+
+static void appIdTestSetup()
+{
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ static SessionAPI sessionAPI = { 0 };
+ static StreamAPI streamAPI = { 0 };
+
+ testFilesPath = getenv("APPID_TESTS_PATH");
+
+ if (testFilesPath == nullptr)
+ {
+ printf("Env variable APPID_TESTS_PATH is not set. Exiting ...\n");
+ exit(-1);
+ }
+
+ strcpy(rnaConfPath, testFilesPath);
+ strcat(rnaConfPath, "/rna.conf");
+
+ _dpd.tokenSplit = mSplit;
+ _dpd.tokenFree = mSplitFree;
+ LogMessage = logMsg;
+ _dpd.errMsg = errMsg;
+ _dpd.debugMsg = debugMsg;
+ _dpd.addProtocolReference = addProtocolReference;
+ _dpd.addPreproc = addPreproc;
+ _dpd.getParserPolicy = getParserPolicy;
+ _dpd.getDefaultPolicy = getDefaultPolicy;
+ _dpd.isAppIdRequired = isAppIdRequired;
+ _dpd.getSnortInstance = getSnortInstance;
+ _dpd.findProtocolReference = findProtocolReference;
+
+ sessionAPI.enable_preproc_all_ports = enable_preproc_all_ports;
+ sessionAPI.get_application_data = get_application_data;
+ sessionAPI.set_application_data = set_application_data;
+ sessionAPI.get_packet_direction = get_packet_direction;
+ sessionAPI.get_session_flags = get_session_flags;
+ sessionAPI.get_session_ip_address = get_session_ip_address;
+ sessionAPI.get_application_protocol_id = get_application_protocol_id;
+ sessionAPI.get_http_xff_precedence = get_http_xff_precedence;
+ _dpd.sessionAPI = &sessionAPI;
+
+ streamAPI.is_session_decrypted = is_session_decrypted;
+ streamAPI.set_application_id = set_application_id;
+ streamAPI.is_session_http2 = is_session_http2;
+ _dpd.streamAPI = &streamAPI;
+
+ _dpd.searchAPI = &searchAPI;
+#endif
+
+ appIdApiInit(&appIdApi);
+}
+
+#ifdef REMOVED_WHILE_NOT_IN_USE
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include <check.h>
+
+#include "parser/mstring.h"
+
+#include "appid_config.h"
+
+#include "external_apis.h"
+#include "fw_appid.h"
+#include "session_file.h"
+
+// FIXIT - this must go when snort2.9.x sf_ip.h changes are ported to snort++
+#include "sfaddr_temp.h"
+
+#if 1 // FIXIT-M hacks
+// not sure where this is defined; outside the appid tree probably
+using ControlDataSendFunc = void (*)();
+#endif
+
+extern void AppIdReload(struct _SnortConfig* sc, char* args, void** new_config);
+extern void* AppIdReloadSwap(struct _SnortConfig* sc, void* swap_config);
+extern void AppIdReloadFree(void* old_context);
+extern int AppIdReconfigure(uint16_t type, const uint8_t* data, uint32_t length,
+ void** new_context,
+ char* statusBuf, int statusBuf_len);
+extern int AppIdReconfigureSwap(uint16_t type, void* new_context, void** old_context);
+extern void AppIdReconfigureFree(uint16_t type, void* old_context, struct _THREAD_ELEMENT* te,
+ ControlDataSendFunc f);
+#endif
+
+#ifdef REMOVED_WHILE_NOT_IN_USE
+extern int processHTTPPacket(Packet* p, AppIdData* session, int direction,
+ HttpParsedHeaders* const headers, const AppIdConfig* pConfig);
+extern void sfiph_build(Packet* p, const void* hdr, int family);
+extern void pickHttpXffAddress(Packet* p, AppIdData* appIdSession,
+ ThirdPartyAppIDAttributeData* attribute_data);
+
+// FIXIT: use APIs instead of using global
+extern AppIdData* pAppIdData;
+
+static char* testFilesPath = nullptr;
+static char rnaConfPath[PATH_MAX] = { 0 };
+
+static void testProcessHttpPacket(const char* useragent, const char* host, const char* referer,
+ const char* trailer)
+{
+ // FIXIT-M J these need to be cleared, probably
+ Packet p;
+ AppIdData session;
+
+ char buf[1024];
+ int bufLen;
+
+ session.common.flags = 0x311380;
+ session.hsession = (decltype(session.hsession))snort_calloc(sizeof(httpSession));
+ if (host)
+ {
+ session.hsession->host = snort_strdup(host);
+ strcpy(buf, "http://");
+ strcat(buf, host);
+ if (trailer)
+ strcat(buf, trailer);
+ else
+ strcat(buf, "/");
+ session.hsession->url = snort_strdup(buf);
+ }
+ if (useragent)
+ session.hsession->useragent = snort_strdup(useragent);
+ if (referer)
+ session.hsession->referer = snort_strdup(referer);
+ session.hsession->uri = snort_strdup("/");
+ session.hsession->cookie = snort_strdup(
+ "s_vi=[CS]v1|25B026B7851D124A-6000012D802520B2[CE]; CG=US:MD:Laurel; mbox=check#true#1336576860|session#1336576799559-724714#1336578660; SelectedEdition=www; rsi_segs_ttn=A09801_10001|A09801_10313; ug="
+ "4faa8b240a5fef0aa5147448c8005347; ugs=1; tnr:usrvtstg01=1336576805411%7C0%7C0%7C1%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C1%7Cf%7C"
+ "1%7C4%7C1336576805411; tnr:sesctmp01=1336576805411; s_cc=true; s_sq=%5B%5BB%5D%5D; adDEmas=R08&broadband&gblx.net&73&gbr&826027&0&10198&-&-&-&15275&; adDEon=true; s_ppv=13");
+ session.serviceAppId = APP_ID_NONE;
+ session.payloadAppId = APP_ID_NONE;
+ session.tpPayloadAppId = 1190;
+ session.scan_flags = 0x26;
+ session.ClientAppId = 0;
+
+ strcpy(buf, "GET / HTTP/1.1\r\n");
+ strcat(buf, "Host: ");
+ strcat(buf, host);
+ strcat(buf, "\r\nUser-Agent: ");
+ strcat(buf, useragent);
+ if (referer)
+ {
+ strcat(buf, "\r\nReferer: ");
+ strcat(buf, referer);
+ }
+ strcat(buf, "\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n");
+ strcat(buf,
+ "Accept-Language: en-us,en;q=0.5\r\nAccept-Encoding: gzip,deflate\r\nAccept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n");
+ strcat(buf, "Keep-Alive: 115\r\nConnection: keep-alive\r\n");
+ bufLen = strlen(buf);
+ strncat(buf,
+ "Cookie: s_vi=[CS]v1|25B026B7851D124A-6000012D802520B2[CE]; CG=US:MD:Laurel; mbox=check#true#1336576860|session#1336576799559-724714#1336578660; SelectedEdition=www; rsi_segs_ttn=A09801_10001|A09801_10313; ug=4faa8b240a5fef0aa5147448c8005347; ugs=1; tnr:usrvtstg01=1336576805411%7C0%7C0%7C1%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C1%7Cf%7C1%7C4%7C1336576805411; tnr:sesctmp01=1336576805411; s_cc=true; s_sq=%5B%5BB%5D%5D; adDEmas=R08&broadband&gblx.net&73&gbr&826027&0&10198&-&-&-&15275&; adDEon=true; s_ppv=13\r\n\r\n",
+ sizeof(buf) - bufLen - 1);
+
+ p.data = (decltype(p.data))snort_strdup(buf);
+ p.dsize = strlen((const char*)p.data);
+
+ processHTTPPacket(&p, &session, APP_ID_FROM_INITIATOR, nullptr, pAppidActiveConfig);
+
+ if (host)
+ {
+ snort_free(session.hsession->host);
+ snort_free(session.hsession->url);
+ }
+ if (referer)
+ snort_free(session.hsession->referer);
+ if (useragent)
+ snort_free(session.hsession->useragent);
+ snort_free(session.hsession->uri);
+ snort_free(session.hsession->cookie);
+ snort_free(session.hsession);
+
+ snort_free((uint8_t*)p.data);
+}
+
+START_TEST(HttpTest)
+{
+ memset(&appidStaticConfig, 0, sizeof(appidStaticConfig));
+
+ strcpy(appidStaticConfig.conf_file, rnaConfPath);
+ strcpy(appidStaticConfig.app_id_detector_path, testFilesPath);
+
+ AppIdCommonInit(&appidStaticConfig);
+
+ testProcessHttpPacket(
+ "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.7) Gecko/20100715 Ubuntu/9.04 (jaunty) Firefox/3.6.7",
+ "www.cnn.com",
+ nullptr, nullptr);
+ testProcessHttpPacket(
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/600.1.17 (KHTML, like Gecko) Version/7.1 Safari/537.85.10",
+ "www.cnn.com",
+ nullptr, nullptr);
+ testProcessHttpPacket(
+ "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)",
+ "www.cnn.com",
+ nullptr, nullptr);
+
+ AppIdCommonFini();
+}
+
+END_TEST
+
+#ifdef REMOVED_WHILE_NOT_IN_USE
+START_TEST(HttpAfterReloadTest)
+{
+ AppIdConfig* pNewConfig = nullptr;
+ AppIdConfig* pOldConfig = nullptr;
+
+ memset(&appidStaticConfig, 0, sizeof(appidStaticConfig));
+
+ strcpy(appidStaticConfig.conf_file, rnaConfPath);
+ strcpy(appidStaticConfig.app_id_detector_path, testFilesPath);
+
+ AppIdCommonInit(&appidStaticConfig);
+
+ AppIdReload(nullptr, nullptr, (void**)&pNewConfig);
+ pOldConfig = AppIdReloadSwap(nullptr, pNewConfig);
+ AppIdReloadFree(pOldConfig);
+
+ testProcessHttpPacket(
+ "Mozilla/5.0 (Windows NT 6.0; rv:2.0) Gecko/20100101 Firefox/4.0 Opera 12.14",
+ "www.cnn.com",
+ nullptr, nullptr);
+ testProcessHttpPacket(
+ "Mozilla/5.0 (BlackBerry; U; BlackBerry 9900; en) AppleWebKit/534.11+ (KHTML, like Gecko) Version/7.1.0.346 Mobile Safari/534.11+",
+ "www.cnn.com",
+ nullptr, nullptr);
+ testProcessHttpPacket(
+ "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36",
+ "www.cnn.com",
+ nullptr, nullptr);
+
+ AppIdCommonFini();
+}
+
+END_TEST START_TEST(HttpAfterReconfigureTest)
+{
+ AppIdConfig* pNewConfig = nullptr;
+ AppIdConfig* pOldConfig = nullptr;
+
+ memset(&appidStaticConfig, 0, sizeof(appidStaticConfig));
+
+ strcpy(appidStaticConfig.conf_file, rnaConfPath);
+ strcpy(appidStaticConfig.app_id_detector_path, testFilesPath);
+
+ AppIdCommonInit(&appidStaticConfig);
+
+ AppIdReconfigure(0, nullptr, 0, (void**)&pNewConfig, nullptr, 0);
+ AppIdReconfigureSwap(0, pNewConfig, (void**)&pOldConfig);
+ AppIdReconfigureFree(0, pOldConfig, nullptr, nullptr);
+
+ testProcessHttpPacket("Wget/1.9.1",
+ "www.cnn.com",
+ nullptr, nullptr);
+ testProcessHttpPacket(
+ "Mozilla/5.0 (Linux; U; Android 4.0.3; ko-kr; LG-L160L Build/IML74K) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
+ "www.cnn.com",
+ nullptr, nullptr);
+
+ AppIdCommonFini();
+}
+
+END_TEST START_TEST(HttpAfterReloadReconfigureTest)
+{
+ AppIdConfig* pNewConfig = nullptr;
+ AppIdConfig* pOldConfig = nullptr;
+
+ memset(&appidStaticConfig, 0, sizeof(appidStaticConfig));
+
+ strcpy(appidStaticConfig.conf_file, rnaConfPath);
+ strcpy(appidStaticConfig.app_id_detector_path, testFilesPath);
+
+ AppIdCommonInit(&appidStaticConfig);
+
+ AppIdReload(nullptr, nullptr, (void**)&pNewConfig);
+ pOldConfig = AppIdReloadSwap(nullptr, pNewConfig);
+ AppIdReloadFree(pOldConfig);
+
+ pNewConfig = nullptr;
+ pOldConfig = nullptr;
+
+ AppIdReconfigure(0, nullptr, 0, (void**)&pNewConfig, nullptr, 0);
+ AppIdReconfigureSwap(0, pNewConfig, (void**)&pOldConfig);
+ AppIdReconfigureFree(0, pOldConfig, nullptr, nullptr);
+
+ testProcessHttpPacket(
+ "Mozilla/5.0 (Linux; U; Android 4.0.3; ko-kr; LG-L160L Build/IML74K) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
+ "www.123.com",
+ "http://www.cnn.com", nullptr);
+ testProcessHttpPacket(
+ "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.7) Gecko/20100715 Ubuntu/9.04 (jaunty) Firefox/3.6.7",
+ "www.cnn.com",
+ nullptr, "/tech?a=1&b=2");
+
+ AppIdCommonFini();
+}
+
+END_TEST START_TEST(HttpXffTest)
+{
+ Packet p = { 0 };
+ AppIdData session = { 0 };
+ httpSession hsession = { 0 };
+ ThirdPartyAppIDAttributeData tpData = { 0 };
+ SFIP_RET status;
+ sfaddr_t* xffAddr = sfaddr_alloc("1.1.1.1", &status);
+
+ // Only X-Forwarded-For
+ session.hsession = &hsession;
+ tpData.numXffFields = 1;
+ tpData.xffFieldValue[0].field = HTTP_XFF_FIELD_X_FORWARDED_FOR;
+ tpData.xffFieldValue[0].value = "1.1.1.1";
+ pickHttpXffAddress(&p, &session, &tpData);
+ ck_assert_int_eq(sfip_compare(session.hsession->xffAddr, xffAddr), SFIP_EQUAL);
+ sfaddr_free(session.hsession->xffAddr);
+
+ // Only True-Client-IP
+ memset(&p, 0, sizeof(p));
+ memset(&session, 0, sizeof(session));
+ memset(&hsession, 0, sizeof(hsession));
+ memset(&tpData, 0, sizeof(tpData));
+ session.hsession = &hsession;
+ tpData.numXffFields = 1;
+ tpData.xffFieldValue[0].field = HTTP_XFF_FIELD_TRUE_CLIENT_IP;
+ tpData.xffFieldValue[0].value = "1.1.1.1";
+ pickHttpXffAddress(&p, &session, &tpData);
+ ck_assert_int_eq(sfip_compare(session.hsession->xffAddr, xffAddr), SFIP_EQUAL);
+ sfaddr_free(session.hsession->xffAddr);
+
+ // X-Forwarded-For and True-Client-IP
+ memset(&p, 0, sizeof(p));
+ memset(&session, 0, sizeof(session));
+ memset(&hsession, 0, sizeof(hsession));
+ memset(&tpData, 0, sizeof(tpData));
+ session.hsession = &hsession;
+ tpData.numXffFields = 2;
+ tpData.xffFieldValue[0].field = HTTP_XFF_FIELD_TRUE_CLIENT_IP;
+ tpData.xffFieldValue[0].value = "2.2.2.2";
+ tpData.xffFieldValue[1].field = HTTP_XFF_FIELD_X_FORWARDED_FOR;
+ tpData.xffFieldValue[1].value = "1.1.1.1";
+ pickHttpXffAddress(&p, &session, &tpData);
+ ck_assert_int_eq(sfip_compare(session.hsession->xffAddr, xffAddr), SFIP_EQUAL);
+ sfaddr_free(session.hsession->xffAddr);
+
+ // Comma-separated list in X-Forwarded-For
+ memset(&p, 0, sizeof(p));
+ memset(&session, 0, sizeof(session));
+ memset(&hsession, 0, sizeof(hsession));
+ memset(&tpData, 0, sizeof(tpData));
+ session.hsession = &hsession;
+ tpData.numXffFields = 1;
+ tpData.xffFieldValue[0].field = HTTP_XFF_FIELD_X_FORWARDED_FOR;
+ tpData.xffFieldValue[0].value = snort_strdup("1.1.1.1, 2.2.2.2");
+ pickHttpXffAddress(&p, &session, &tpData);
+ ck_assert_int_eq(sfip_compare(session.hsession->xffAddr, xffAddr), SFIP_EQUAL);
+ snort_free(tpData.xffFieldValue[0].value);
+ sfaddr_free(session.hsession->xffAddr);
+
+ // Custom XFF
+ static char* defaultXffPrecedence[] = { "Custom-XFF", HTTP_XFF_FIELD_X_FORWARDED_FOR,
+ HTTP_XFF_FIELD_TRUE_CLIENT_IP };
+ memset(&p, 0, sizeof(p));
+ memset(&session, 0, sizeof(session));
+ memset(&hsession, 0, sizeof(hsession));
+ memset(&tpData, 0, sizeof(tpData));
+ session.hsession = &hsession;
+ session.hsession->xffPrecedence = defaultXffPrecedence;
+ session.hsession->numXffFields = 3;
+ tpData.numXffFields = 2;
+ tpData.xffFieldValue[0].field = HTTP_XFF_FIELD_X_FORWARDED_FOR;
+ tpData.xffFieldValue[0].value = "2.2.2.2";
+ tpData.xffFieldValue[1].field = "Custom-XFF";
+ tpData.xffFieldValue[1].value = "1.1.1.1";
+ pickHttpXffAddress(&p, &session, &tpData);
+ ck_assert_int_eq(sfip_compare(session.hsession->xffAddr, xffAddr), SFIP_EQUAL);
+ sfaddr_free(session.hsession->xffAddr);
+
+ sfaddr_free(xffAddr);
+
+ snort_free((uint8_t*)p.data);
+}
+
+#endif
+
+static void sessionTcaseSetup(void)
+{
+ memset(&appidStaticConfig, 0, sizeof(appidStaticConfig));
+
+ strcpy(appidStaticConfig.conf_file, rnaConfPath);
+ strcpy(appidStaticConfig.app_id_detector_path, testFilesPath);
+
+ AppIdCommonInit(&appidStaticConfig);
+}
+
+static void sessionTcaseClean(void)
+{
+ AppIdCommonFini();
+}
+
+static Suite* setupAppIdSuite(void)
+{
+ Suite* appIdSuite;
+ TCase* frameworkTcase;
+ TCase* httpTcase;
+ TCase* sessionTcase;
+
+ appIdSuite = suite_create("AppId");
+
+ // Create Framework test case
+ frameworkTcase = tcase_create("FrameworkTestCase");
+ tcase_add_checked_fixture(frameworkTcase, nullptr, nullptr);
+
+ // Add tests to Framework test case
+ tcase_add_test(frameworkTcase, ConfigParseTest);
+ tcase_add_test(frameworkTcase, InitFiniTest);
+ tcase_add_test(frameworkTcase, ReloadTest);
+ tcase_add_test(frameworkTcase, ReconfigureTest);
+
+ suite_add_tcase(appIdSuite, frameworkTcase);
+
+ // Create Http test case
+ httpTcase = tcase_create("HttpTestCase");
+ tcase_add_checked_fixture(httpTcase, nullptr, nullptr);
+
+ // Add tests to Http test case
+ tcase_add_test(httpTcase, HttpTest);
+ tcase_add_test(httpTcase, HttpAfterReloadTest);
+ tcase_add_test(httpTcase, HttpAfterReconfigureTest);
+ tcase_add_test(httpTcase, HttpAfterReloadReconfigureTest);
+ tcase_add_test(httpTcase, HttpXffTest);
+
+ suite_add_tcase(appIdSuite, httpTcase);
+
+ // Create Session test case
+ sessionTcase = tcase_create("SessionTestCase");
+ tcase_add_checked_fixture(sessionTcase, nullptr, nullptr);
+
+ // Add tests to Session test case
+ tcase_add_test(sessionTcase, AimSessionTest);
+ tcase_add_test(sessionTcase, CnnSessionTest);
+ tcase_add_test(sessionTcase, DnsSessionTest);
+ tcase_add_test(sessionTcase, ImapSessionTest);
+ tcase_add_test(sessionTcase, MdnsSessionTest);
+ tcase_add_test(sessionTcase, MsnSessionTest);
+ tcase_add_test(sessionTcase, NetbiosNsSessionTest);
+ tcase_add_test(sessionTcase, NetbiosSsSessionTest);
+ tcase_add_test(sessionTcase, PatternSessionTest);
+ tcase_add_test(sessionTcase, Pop3SessionTest);
+ tcase_add_test(sessionTcase, RfbSessionTest);
+ tcase_add_test(sessionTcase, RtpSessionTest);
+ tcase_add_test(sessionTcase, SmtpSessionTest);
+ tcase_add_test(sessionTcase, TimbuktuSessionTest);
+ tcase_add_test(sessionTcase, WebexSessionTest);
+ tcase_add_test(sessionTcase, YmSessionTest);
+
+ suite_add_tcase(appIdSuite, sessionTcase);
+
+ return appIdSuite;
+}
+
+#endif
+
+int main()
+{
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ int opt, debug = 0;
+ int numberFailed;
+ Suite* appIdSuite;
+ SRunner* appIdRunner;
+
+ while ((opt = getopt(argc, argv, "dh")) != -1)
+ {
+ switch (opt)
+ {
+ case 'd':
+ debug = 1;
+ break;
+ case 'h':
+ printf(
+ "Usage:\n\
+ -d: Run test in no fork mode for debugging in gdb.\n\
+ -h: This text.\n");
+ return EXIT_SUCCESS;
+ }
+ }
+#endif
+
+ appIdTestSetup();
+
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ // Create a test runner for AppId suite
+ appIdSuite = setupAppIdSuite();
+ appIdRunner = srunner_create(appIdSuite);
+
+ if (debug)
+ {
+ srunner_set_fork_status(appIdRunner, CK_NOFORK);
+ }
+
+ // Set test results format to TAP and specify file name
+ srunner_set_tap(appIdRunner, "AppIdTests.tap~");
+
+ // Run test cases in AppId suite
+ srunner_run(appIdRunner, nullptr, "FrameworkTestCase", CK_NORMAL);
+
+ system("cp AppIdTests.tap~ AppIdTests.tap");
+
+ srunner_run(appIdRunner, nullptr, "HttpTestCase", CK_NORMAL);
+
+ system("cat AppIdTests.tap~ >> AppIdTests.tap");
+
+ sessionTcaseSetup();
+ srunner_run(appIdRunner, nullptr, "SessionTestCase", CK_NORMAL);
+ sessionTcaseClean();
+
+ system("cat AppIdTests.tap~ >> AppIdTests.tap");
+
+ numberFailed = srunner_ntests_failed(appIdRunner);
+ srunner_free(appIdRunner);
+ return (numberFailed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+#endif
+}
+
--- /dev/null
+#
+# Configuration generated for NetworkDiscovery
+#
+# FIXIT - Look into converting these configuration options to lua
+#
+config ModuleDirectory /var/sf/detection_engines/812d18e2-1851-11e6-9201-d085f23f50f8/libs/rna
+config OutputMethod serial -f rna.bin -t 86400 -s 20
+config Database sfsnort correlator correlator
+
+config MaxHostClientApps 16
+config MaxHostServices 100
+config MaxPayloads 100
+config MaxHostServiceInfo 16
+
+protoid BannerGrab 0
+pnd UpdateTimeout 3600
+userConfig captureFailedLoginAttempts 1
+# START Export Rule - 1 (5e30e278-1852-11e6-a7db-e1e48cc1c54d)
+# Device Analyze
+config AnalyzeApplication 0.0.0.0/0 -1
+config AnalyzeApplication ::/0 -1
+# Device Source Service Exclusion
+# Device Destination Service Exclusion
+# END
+
+
--- /dev/null
+#include <stdio.h>
+#include "session_file.h"
+
+static void sessionFileAdd(void* session, SessionFileData* data);
+static SessionFileData* sessionFileFind(void* session);
+static void sessionDataDump(FILE* file, SessionControlBlock* scb);
+static void packetDataDump(FILE* file, uint32_t packetCount, Packet* pkt);
+static void sessionDataRead(FILE* file, SessionControlBlock* scb);
+static int packetDataRead(FILE* file, Packet* pkt, HttpParsedHeaders** pHttpHeader);
+static void readHttpHeaderItem(FILE* file, HEADER_LOCATION* headerLocation);
+
+static SFXHASH* sessionFiles = nullptr;
+
+void sessionFileInit(void)
+{
+ sessionFiles = sfxhash_new(2048,
+ sizeof(void*),
+ sizeof(SessionFileData),
+ 0,
+ 0,
+ nullptr,
+ nullptr,
+ 0);
+}
+
+void sessionFileFini(void)
+{
+ SFXHASH_NODE* node;
+ SessionFileData* data;
+
+ for (node = sfxhash_findfirst(sessionFiles);
+ node;
+ node = sfxhash_findnext(sessionFiles))
+ {
+ data = (SessionFileData*)node->data;
+ fclose(data->file);
+ }
+
+ sfxhash_delete(sessionFiles);
+ sessionFiles = nullptr;
+}
+
+FILE* sessionFileProcess(Packet* pkt)
+{
+ static uint32_t packetCount = 0;
+ static uint32_t sessionCount = 0;
+ SessionFileData* pSessionFileData;
+ SessionFileData sessionFileData;
+ SessionControlBlock* scb = (SessionControlBlock*)pkt->stream_session;
+
+ packetCount++;
+
+ if (!scb)
+ {
+ printf("Ignoring packet %d\n", packetCount);
+ return nullptr;
+ }
+
+ pSessionFileData = sessionFileFind(scb);
+
+ if (!pSessionFileData)
+ {
+ pSessionFileData = &sessionFileData;
+
+ sessionCount++;
+ sprintf(pSessionFileData->fileName, "session%d.ssn", sessionCount);
+ pSessionFileData->file = fopen(pSessionFileData->fileName, "w");
+ pSessionFileData->packetCount = 1;
+
+ sessionFileAdd(scb, pSessionFileData);
+ }
+ else
+ {
+ pSessionFileData->packetCount++;
+ }
+
+ sessionDataDump(pSessionFileData->file, scb);
+
+ packetDataDump(pSessionFileData->file, pSessionFileData->packetCount, pkt);
+
+ return pSessionFileData->file;
+}
+
+void sessionFileProcessHttp(Packet* pkt, HttpParsedHeaders* headers)
+{
+ FILE* file = sessionFileProcess(pkt);
+
+ if (file == nullptr)
+ return;
+
+ if (headers->host.len > 0)
+ {
+ fprintf(file, " %u %d ", PACKET_HTTP_HOST, headers->host.len);
+ fwrite(headers->host.start, 1, headers->host.len, file);
+ }
+ if (headers->url.len > 0)
+ {
+ fprintf(file, "\n %u %d ", PACKET_HTTP_URL, headers->url.len);
+ fwrite(headers->url.start, 1, headers->url.len, file);
+ }
+ if (headers->method.len > 0)
+ {
+ fprintf(file, "\n %u %d ", PACKET_HTTP_METHOD, headers->method.len);
+ fwrite(headers->method.start, 1, headers->method.len, file);
+ }
+ if (headers->userAgent.len > 0)
+ {
+ fprintf(file, "\n %u %d ", PACKET_HTTP_USER_AGENT, headers->userAgent.len);
+ fwrite(headers->userAgent.start, 1, headers->userAgent.len, file);
+ }
+ if (headers->referer.len > 0)
+ {
+ fprintf(file, "\n %u %d ", PACKET_HTTP_REFERER, headers->referer.len);
+ fwrite(headers->referer.start, 1, headers->referer.len, file);
+ }
+ if (headers->via.len > 0)
+ {
+ fprintf(file, "\n %u %d ", PACKET_HTTP_VIA, headers->via.len);
+ fwrite(headers->via.start, 1, headers->via.len, file);
+ }
+ if (headers->responseCode.len > 0)
+ {
+ fprintf(file, "\n %u %d ", PACKET_HTTP_RESPONSE_CODE, headers->responseCode.len);
+ fwrite(headers->responseCode.start, 1, headers->responseCode.len, file);
+ }
+ if (headers->server.len > 0)
+ {
+ fprintf(file, "\n %u %d ", PACKET_HTTP_SERVER, headers->server.len);
+ fwrite(headers->server.start, 1, headers->server.len, file);
+ }
+ if (headers->xWorkingWith.len > 0)
+ {
+ fprintf(file, "\n %u %d ", PACKET_HTTP_X_WORKING_WITH, headers->xWorkingWith.len);
+ fwrite(headers->xWorkingWith.start, 1, headers->xWorkingWith.len, file);
+ }
+ if (headers->contentType.len > 0)
+ {
+ fprintf(file, "\n %u %d ", PACKET_HTTP_CONTENT_TYPE, headers->contentType.len);
+ fwrite(headers->contentType.start, 1, headers->contentType.len, file);
+ }
+ fprintf(file, "\n");
+}
+
+void sessionFileReadSession(FILE* file, SessionControlBlock* scb)
+{
+ char buf[16];
+
+ while (true)
+ {
+ fscanf(file, "%s\n", buf);
+
+ if (strcmp(buf, "Session:") == 0)
+ {
+ sessionDataRead(file, scb);
+ return;
+ }
+ }
+}
+
+int sessionFileReadPacket(FILE* file, Packet* pkt, HttpParsedHeaders** pHttpHeader)
+{
+ char buf[16];
+
+ while (true)
+ {
+ fscanf(file, "%s\n", buf);
+
+ if (strncmp(buf, "Packet", 6) == 0)
+ {
+ fgets(buf, 16, file);
+ return packetDataRead(file, pkt, pHttpHeader);
+ }
+ }
+}
+
+static void sessionFileAdd(void* session, SessionFileData* data)
+{
+ sfxhash_add(sessionFiles, &session, data);
+}
+
+static SessionFileData* sessionFileFind(void* session)
+{
+ return (SessionFileData*)sfxhash_find(sessionFiles, &session);
+}
+
+static void sessionDataDump(FILE* file, SessionControlBlock* scb)
+{
+ fprintf(file, "Session:\n");
+ fprintf(file, " %u %08X %08X %08X %08X\n", SESSION_CLIENT_IP_IA32,
+ scb->client_ip.ia32[0],
+ scb->client_ip.ia32[1],
+ scb->client_ip.ia32[2],
+ scb->client_ip.ia32[3]);
+ fprintf(file, " %u %u\n", SESSION_CLIENT_PORT, scb->client_port);
+ fprintf(file, " %u %u\n", SESSION_HA_STATE_SESSION_FLAGS, scb->ha_state.session_flags);
+}
+
+static void packetDataDump(FILE* file, uint32_t packetCount, Packet* pkt)
+{
+ fprintf(file, "Packet %d:\n", packetCount);
+
+ if (pkt->pkt_header)
+ {
+ fprintf(file, " %u %u\n", PACKET_PKT_HEADER_TS_TV_SEC, (unsigned
+ int)pkt->pkt_header->ts.tv_sec);
+ fprintf(file, " %u %d\n", PACKET_PKT_HEADER_INGRESS_GROUP,
+ pkt->pkt_header->ingress_group);
+ fprintf(file, " %u %u\n", PACKET_PKT_HEADER_PKTLEN, pkt->pkt_header->pktlen);
+ }
+
+ if (pkt->tcp_header)
+ {
+ fprintf(file, " %u %u\n", PACKET_TCP_HEADER_SOURCE_PORT, pkt->tcp_header->source_port);
+ fprintf(file, " %u %u\n", PACKET_TCP_HEADER_FLAGS, pkt->tcp_header->flags);
+ }
+
+ if (pkt->udp_header)
+ {
+ fprintf(file, " %u %u\n", PACKET_UDP_HEADER_SOURCE_PORT, pkt->udp_header->source_port);
+ }
+
+ if (pkt->is_ip4())
+ {
+ fprintf(file, " %u %08X %08X %08X %08X\n", PACKET_IP4H_IP_ADDRS_IP_SRC_IA32,
+ pkt->ip4h->ip_addrs->ip_src.ia32[0],
+ pkt->ip4h->ip_addrs->ip_src.ia32[1],
+ pkt->ip4h->ip_addrs->ip_src.ia32[2],
+ pkt->ip4h->ip_addrs->ip_src.ia32[3]);
+ fprintf(file, " %u %u\n", PACKET_IP4H_IP_ADDRS_IP_SRC_FAMILY,
+ pkt->ip4h->ip_addrs->ip_src.family);
+ fprintf(file, " %u %08X %08X %08X %08X\n", PACKET_IP4H_IP_ADDRS_IP_DST_IA32,
+ pkt->ip4h->ip_addrs->ip_dst.ia32[0],
+ pkt->ip4h->ip_addrs->ip_dst.ia32[1],
+ pkt->ip4h->ip_addrs->ip_dst.ia32[2],
+ pkt->ip4h->ip_addrs->ip_dst.ia32[3]);
+ fprintf(file, " %u %u\n", PACKET_IP4H_IP_ADDRS_IP_DST_FAMILY,
+ pkt->ip4h->ip_addrs->ip_dst.family);
+ fprintf(file, " %u %u\n", PACKET_IP4H_IP_PROTO, pkt->ip4h->ip_proto);
+ }
+
+ if (pkt->ip6h)
+ {
+ fprintf(file, " %u %08X %08X %08X %08X\n", PACKET_IP6H_IP_ADDRS_IP_SRC_IA32,
+ pkt->ip6h->ip_addrs->ip_src.ia32[0],
+ pkt->ip6h->ip_addrs->ip_src.ia32[1],
+ pkt->ip6h->ip_addrs->ip_src.ia32[2],
+ pkt->ip6h->ip_addrs->ip_src.ia32[3]);
+ fprintf(file, " %u %u\n", PACKET_IP6H_IP_ADDRS_IP_SRC_FAMILY,
+ pkt->ip6h->ip_addrs->ip_src.family);
+ fprintf(file, " %u %08X %08X %08X %08X\n", PACKET_IP6H_IP_ADDRS_IP_DST_IA32,
+ pkt->ip6h->ip_addrs->ip_dst.ia32[0],
+ pkt->ip6h->ip_addrs->ip_dst.ia32[1],
+ pkt->ip6h->ip_addrs->ip_dst.ia32[2],
+ pkt->ip6h->ip_addrs->ip_dst.ia32[3]);
+ fprintf(file, " %u %u\n", PACKET_IP6H_IP_ADDRS_IP_DST_FAMILY,
+ pkt->ip6h->ip_addrs->ip_dst.family);
+ }
+
+ fprintf(file, " %u %u\n", PACKET_FAMILY, pkt->family);
+ fprintf(file, " %u %08X\n", PACKET_FLAGS, pkt->flags);
+ fprintf(file, " %u %u\n", PACKET_SRC_PORT, pkt->src_port);
+ fprintf(file, " %u %u\n", PACKET_DST_PORT, pkt->dst_port);
+ if (pkt->payload_size)
+ {
+ fprintf(file, " %u %u ", PACKET_PAYLOAD, pkt->payload_size);
+ fwrite(pkt->payload, 1, pkt->payload_size, file);
+ fprintf(file, "\n");
+ }
+}
+
+void sessionDataRead(FILE* file, SessionControlBlock* scb)
+{
+ int match;
+ uint32_t type;
+
+ while (true)
+ {
+ match = fscanf(file, "%u", &type);
+
+ if ((match == EOF) || (match == 0))
+ return;
+
+ switch (type)
+ {
+ case SESSION_CLIENT_IP_IA32:
+ fscanf(file, "%x", &scb->client_ip.ia32[0]);
+ fscanf(file, "%x", &scb->client_ip.ia32[1]);
+ fscanf(file, "%x", &scb->client_ip.ia32[2]);
+ fscanf(file, "%x\n", &scb->client_ip.ia32[3]);
+ break;
+ case SESSION_CLIENT_PORT:
+ fscanf(file, "%u\n", (unsigned int*)&scb->client_port);
+ break;
+ case SESSION_HA_STATE_SESSION_FLAGS:
+ fscanf(file, "%u\n", &scb->ha_state.session_flags);
+ break;
+ default:
+ printf("Unknown session field\n");
+ break;
+ }
+ }
+}
+
+// FIXIT - M Must check to ensure memory allocated by snort_calloc's below is freed when
+// the tests complete
+static int packetDataRead(FILE* file, Packet* pkt, HttpParsedHeaders** pHttpHeader)
+{
+ static SFDAQ_PktHdr_t pkt_header = { 0 };
+ static TCPHeader tcp_header = { 0 };
+ static UDPHeader udp_header = { 0 };
+ static IP4Hdr ip4h = { 0 };
+ static IP6Hdr ip6h = { 0 };
+ static IPAddresses ip4_addrs = { 0 };
+ static IPAddresses ip6_addrs = { 0 };
+ int match;
+ uint32_t type;
+
+ memset(&pkt_header, 0, sizeof(pkt_header));
+ memset(&tcp_header, 0, sizeof(tcp_header));
+ memset(&udp_header, 0, sizeof(udp_header));
+ memset(&ip4h, 0, sizeof(ip4h));
+ memset(&ip6h, 0, sizeof(ip6h));
+ memset(&ip4_addrs, 0, sizeof(ip4_addrs));
+ memset(&ip6_addrs, 0, sizeof(ip6_addrs));
+
+ while (true)
+ {
+ match = fscanf(file, "%u", &type);
+
+ if (match == EOF)
+ return -1;
+ if (match == 0)
+ return 0;
+
+ switch (type)
+ {
+ case PACKET_PKT_HEADER_TS_TV_SEC:
+ pkt->pkt_header = &pkt_header;
+ fscanf(file, "%u\n", (unsigned int*)&pkt_header.ts.tv_sec);
+ break;
+ case PACKET_PKT_HEADER_INGRESS_GROUP:
+ pkt->pkt_header = &pkt_header;
+ fscanf(file, "%u\n", &pkt_header.ingress_group);
+ break;
+ case PACKET_PKT_HEADER_PKTLEN:
+ pkt->pkt_header = &pkt_header;
+ fscanf(file, "%u\n", &pkt_header.pktlen);
+ break;
+ case PACKET_TCP_HEADER_SOURCE_PORT:
+ pkt->tcp_header = &tcp_header;
+ fscanf(file, "%u\n", (unsigned int*)&tcp_header.source_port);
+ break;
+ case PACKET_TCP_HEADER_FLAGS:
+ pkt->tcp_header = &tcp_header;
+ fscanf(file, "%u\n", (unsigned int*)&tcp_header.flags);
+ break;
+ case PACKET_UDP_HEADER_SOURCE_PORT:
+ pkt->udp_header = &udp_header;
+ fscanf(file, "%u\n", (unsigned int*)&udp_header.source_port);
+ break;
+ case PACKET_IP4H_IP_ADDRS_IP_SRC_IA32:
+ ip4h.ip_addrs = &ip4_addrs;
+ pkt->ip4h = &ip4h;
+
+ fscanf(file, "%x", &ip4_addrs.ip_src.ia32[0]);
+ fscanf(file, "%x", &ip4_addrs.ip_src.ia32[1]);
+ fscanf(file, "%x", &ip4_addrs.ip_src.ia32[2]);
+ fscanf(file, "%x\n", &ip4_addrs.ip_src.ia32[3]);
+ break;
+ case PACKET_IP4H_IP_ADDRS_IP_SRC_FAMILY:
+ ip4h.ip_addrs = &ip4_addrs;
+ pkt->ip4h = &ip4h;
+
+ fscanf(file, "%u\n", (unsigned int*)&ip4_addrs.ip_src.family);
+ break;
+ case PACKET_IP4H_IP_ADDRS_IP_DST_IA32:
+ ip4h.ip_addrs = &ip4_addrs;
+ pkt->ip4h = &ip4h;
+
+ fscanf(file, "%x", &ip4_addrs.ip_dst.ia32[0]);
+ fscanf(file, "%x", &ip4_addrs.ip_dst.ia32[1]);
+ fscanf(file, "%x", &ip4_addrs.ip_dst.ia32[2]);
+ fscanf(file, "%x\n", &ip4_addrs.ip_dst.ia32[3]);
+ break;
+ case PACKET_IP4H_IP_ADDRS_IP_DST_FAMILY:
+ ip4h.ip_addrs = &ip4_addrs;
+ pkt->ip4h = &ip4h;
+
+ fscanf(file, "%u\n", (unsigned int*)&ip4_addrs.ip_dst.family);
+ break;
+ case PACKET_IP4H_IP_PROTO:
+ ip4h.ip_addrs = &ip4_addrs;
+ pkt->ip4h = &ip4h;
+
+ fscanf(file, "%u\n", (unsigned int*)&ip4h.ip_proto);
+ break;
+ case PACKET_IP6H_IP_ADDRS_IP_SRC_IA32:
+ ip6h.ip_addrs = &ip6_addrs;
+ pkt->ip6h = &ip6h;
+
+ fscanf(file, "%x", &ip6_addrs.ip_src.ia32[0]);
+ fscanf(file, "%x", &ip6_addrs.ip_src.ia32[1]);
+ fscanf(file, "%x", &ip6_addrs.ip_src.ia32[2]);
+ fscanf(file, "%x\n", &ip6_addrs.ip_src.ia32[3]);
+ break;
+ case PACKET_IP6H_IP_ADDRS_IP_SRC_FAMILY:
+ ip6h.ip_addrs = &ip6_addrs;
+ pkt->ip6h = &ip6h;
+
+ fscanf(file, "%u\n", (unsigned int*)&ip6_addrs.ip_src.family);
+ break;
+ case PACKET_IP6H_IP_ADDRS_IP_DST_IA32:
+ ip6h.ip_addrs = &ip6_addrs;
+ pkt->ip6h = &ip6h;
+
+ fscanf(file, "%x", &ip6_addrs.ip_dst.ia32[0]);
+ fscanf(file, "%x", &ip6_addrs.ip_dst.ia32[1]);
+ fscanf(file, "%x", &ip6_addrs.ip_dst.ia32[2]);
+ fscanf(file, "%x\n", &ip6_addrs.ip_dst.ia32[3]);
+ break;
+ case PACKET_IP6H_IP_ADDRS_IP_DST_FAMILY:
+ ip6h.ip_addrs = &ip6_addrs;
+ pkt->ip6h = &ip6h;
+
+ fscanf(file, "%u\n", (unsigned int*)&ip6_addrs.ip_dst.family);
+ break;
+ case PACKET_FAMILY:
+ fscanf(file, "%u\n", &pkt->family);
+ break;
+ case PACKET_FLAGS:
+ fscanf(file, "%x\n", &pkt->flags);
+ break;
+ case PACKET_SRC_PORT:
+ fscanf(file, "%u\n", (unsigned int*)&pkt->src_port);
+ break;
+ case PACKET_DST_PORT:
+ fscanf(file, "%u\n", (unsigned int*)&pkt->dst_port);
+ break;
+ case PACKET_PAYLOAD:
+ fscanf(file, "%u", (unsigned int*)&pkt->payload_size);
+ fgetc(file);
+ pkt->payload = snort_calloc(sizeof(char)* pkt->payload_size);
+ fread((uint8_t*)pkt->payload, 1, pkt->payload_size, file);
+ break;
+ case PACKET_HTTP_HOST:
+ if (!(*pHttpHeader))
+ *pHttpHeader = snort_calloc(sizeof(HttpParsedHeaders));
+ readHttpHeaderItem(file, &(*pHttpHeader)->host);
+ break;
+ case PACKET_HTTP_URL:
+ if (!(*pHttpHeader))
+ *pHttpHeader = snort_calloc(sizeof(HttpParsedHeaders));
+ readHttpHeaderItem(file, &(*pHttpHeader)->url);
+ break;
+ case PACKET_HTTP_METHOD:
+ if (!(*pHttpHeader))
+ *pHttpHeader = snort_calloc(sizeof(HttpParsedHeaders));
+ readHttpHeaderItem(file, &(*pHttpHeader)->method);
+ break;
+ case PACKET_HTTP_USER_AGENT:
+ if (!(*pHttpHeader))
+ *pHttpHeader = snort_calloc(sizeof(HttpParsedHeaders));
+ readHttpHeaderItem(file, &(*pHttpHeader)->userAgent);
+ break;
+ case PACKET_HTTP_REFERER:
+ if (!(*pHttpHeader))
+ *pHttpHeader = snort_calloc(sizeof(HttpParsedHeaders));
+ readHttpHeaderItem(file, &(*pHttpHeader)->referer);
+ break;
+ case PACKET_HTTP_VIA:
+ if (!(*pHttpHeader))
+ *pHttpHeader = snort_calloc(sizeof(HttpParsedHeaders));
+ readHttpHeaderItem(file, &(*pHttpHeader)->via);
+ break;
+ case PACKET_HTTP_RESPONSE_CODE:
+ if (!(*pHttpHeader))
+ *pHttpHeader = snort_calloc(sizeof(HttpParsedHeaders));
+ readHttpHeaderItem(file, &(*pHttpHeader)->responseCode);
+ break;
+ case PACKET_HTTP_SERVER:
+ if (!(*pHttpHeader))
+ *pHttpHeader = snort_calloc(sizeof(HttpParsedHeaders));
+ readHttpHeaderItem(file, &(*pHttpHeader)->server);
+ break;
+ case PACKET_HTTP_X_WORKING_WITH:
+ if (!(*pHttpHeader))
+ *pHttpHeader = snort_calloc(sizeof(HttpParsedHeaders));
+ readHttpHeaderItem(file, &(*pHttpHeader)->xWorkingWith);
+ break;
+ case PACKET_HTTP_CONTENT_TYPE:
+ if (!(*pHttpHeader))
+ *pHttpHeader = snort_calloc(sizeof(HttpParsedHeaders));
+ readHttpHeaderItem(file, &(*pHttpHeader)->contentType);
+ break;
+ default:
+ printf("Unknown packet field\n");
+ break;
+ }
+ }
+}
+
+static void readHttpHeaderItem(FILE* file, HEADER_LOCATION* headerLocation)
+{
+ uint8_t* start;
+
+ fscanf(file, "%u", &headerLocation->len);
+ start = snort_calloc(headerLocation->len + 1);
+ fgetc(file);
+ fread(start, 1, headerLocation->len, file);
+ start[headerLocation->len] = '\0';
+ headerLocation->start = start;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2015-2016 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.
+//--------------------------------------------------------------------------
+
+// session_file.h author Shravan Rangarajuvenkata <shrarang@cisco.com>
+
+#ifndef SESSION_FILE_H
+#define SESSION_FILE_H
+
+#include <stdio.h>
+
+// FIXIT - this must go when snort2.9.x sf_ip.h changes are ported to snort++
+#include "sfaddr_temp.h"
+
+#define MAX_APP_PROTOCOL_ID 4
+
+struct HttpParsedHeaders;
+struct Packet;
+struct SessionKey;
+struct StreamAppData;
+
+enum SessionField
+{
+ SESSION_CLIENT_IP_IA32 = 1000,
+ SESSION_CLIENT_PORT,
+ SESSION_HA_STATE_SESSION_FLAGS
+};
+
+enum tPktField
+{
+ PACKET_PKT_HEADER_TS_TV_SEC = 1000,
+ PACKET_PKT_HEADER_INGRESS_GROUP,
+ PACKET_PKT_HEADER_PKTLEN,
+
+ PACKET_TCP_HEADER_SOURCE_PORT = 2000,
+ PACKET_TCP_HEADER_FLAGS,
+ PACKET_UDP_HEADER_SOURCE_PORT,
+
+ PACKET_IP4H_IP_ADDRS_IP_SRC_IA32 = 3000,
+ PACKET_IP4H_IP_ADDRS_IP_SRC_FAMILY,
+ PACKET_IP4H_IP_ADDRS_IP_DST_IA32,
+ PACKET_IP4H_IP_ADDRS_IP_DST_FAMILY,
+ PACKET_IP4H_IP_PROTO,
+
+ PACKET_FAMILY = 4000,
+ PACKET_FLAGS,
+ PACKET_SRC_PORT,
+ PACKET_DST_PORT,
+ PACKET_PAYLOAD,
+
+ PACKET_HTTP_HOST = 5000,
+ PACKET_HTTP_URL,
+ PACKET_HTTP_METHOD,
+ PACKET_HTTP_USER_AGENT,
+ PACKET_HTTP_REFERER,
+ PACKET_HTTP_VIA,
+ PACKET_HTTP_RESPONSE_CODE,
+ PACKET_HTTP_SERVER,
+ PACKET_HTTP_X_WORKING_WITH,
+ PACKET_HTTP_CONTENT_TYPE,
+
+ PACKET_IP6H_IP_ADDRS_IP_SRC_IA32 = 6000,
+ PACKET_IP6H_IP_ADDRS_IP_SRC_FAMILY,
+ PACKET_IP6H_IP_ADDRS_IP_DST_IA32,
+ PACKET_IP6H_IP_ADDRS_IP_DST_FAMILY
+};
+
+struct SessionFileData
+{
+ uint32_t packetCount;
+ FILE* file;
+ char fileName[16];
+};
+
+#ifdef MPLS
+struct MPLS_Hdr
+{
+ uint16_t length;
+ uint8_t* start;
+};
+#endif
+
+// FIXIT-M: Temporary structs and defines for initial appid port.
+using tSfPolicyId = int;
+#define SE_MAX 255
+struct StreamHAState { };
+// END FIXIT-M
+
+struct SessionControlBlock
+{
+ SessionKey* key;
+
+ //MemBucket *proto_specific_data;
+ void* proto_specific_data;
+ StreamAppData* appDataList;
+
+ //MemBucket *flowdata; /* add flowbits */
+ void* flowdata; /* add flowbits */
+
+ long last_data_seen;
+ uint64_t expire_time;
+
+ tSfPolicyId napPolicyId;
+ tSfPolicyId ipsPolicyId;
+ bool ips_os_selected;
+ //SessionConfiguration *session_config;
+ void* session_config;
+ void* stream_config;
+ void* proto_policy;
+
+ //PreprocEnableMask enabled_pps;
+ uint32_t enabled_pps;
+ //PreprocEvalFuncNode *initial_pp;
+ void* initial_pp;
+
+ uint16_t session_state;
+ uint8_t handler[SE_MAX];
+ sfaddr_t client_ip; // FIXTHIS family and bits should be changed to uint16_t
+ sfaddr_t server_ip; // or uint8_t to reduce sizeof from 24 to 20
+ uint16_t client_port;
+ uint16_t server_port;
+ bool port_guess;
+
+ uint8_t protocol;
+
+#ifdef ACTIVE_RESPONSE
+ uint8_t response_count;
+#endif
+
+ uint8_t inner_client_ttl;
+ uint8_t inner_server_ttl;
+ uint8_t outer_client_ttl;
+ uint8_t outer_server_ttl;
+
+ StreamHAState ha_state;
+ StreamHAState cached_ha_state;
+
+#ifdef ENABLE_HA
+ struct timeval ha_next_update;
+ uint8_t ha_pending_mask;
+ uint8_t ha_flags;
+#endif
+
+ bool session_established;
+ bool new_session;
+
+ // pointers for linking into list of oneway sessions
+ struct _SessionControlBlock* ows_prev;
+ struct _SessionControlBlock* ows_next;
+ bool in_oneway_list;
+
+ int16_t app_protocol_id[MAX_APP_PROTOCOL_ID];
+
+#ifdef MPLS
+ MPLS_Hdr* clientMplsHeader;
+ MPLS_Hdr* serverMplsHeader;
+#endif
+};
+
+void sessionFileInit();
+void sessionFileFini();
+FILE* sessionFileProcess(Packet*);
+void sessionFileProcessHttp(Packet*, HttpParsedHeaders*);
+void sessionFileReadSession(FILE*, SessionControlBlock*);
+int sessionFileReadPacket(FILE*, Packet*, HttpParsedHeaders**);
+
+#endif
+
--- /dev/null
+/* $Id: sf_iph.c,v 1.2 2015/03/25 14:45:18 andrbake Exp $ */
+/****************************************************************************
+ *
+ * Copyright (C) 2014 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 <string.h>
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "decode.h"
+
+// FIXIT - this must go when snort2.9.x sf_ip.h changes are ported to snort++
+#include "sfaddr_temp.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)
+{
+ if (!p)
+ return;
+
+ _set_callbacks(p, family, CALLBACK_IP);
+}
+
+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
+
--- /dev/null
+---------------------------------------------------------------------------
+-- Snort++ prototype configuration
+---------------------------------------------------------------------------
+
+---------------------------------------------------------------------------
+-- setup environment
+---------------------------------------------------------------------------
+-- given:
+-- export DIR=/install/path
+-- configure --prefix=$DIR
+-- make install
+--
+-- then:
+-- export LUA_PATH=$DIR/include/snort/lua/?.lua\;\;
+-- export SNORT_LUA_PATH=$DIR/conf/
+---------------------------------------------------------------------------
+
+
+
+require("snort_config")
+
+dir = os.getenv('SNORT_LUA_PATH')
+
+if ( not dir ) then
+ dir = '.'
+end
+
+dofile(dir .. '/snort_defaults.lua')
+
+
+appid =
+{
+ conf = 'rna.conf',
+ memcap = 15856401,
+ app_detector_dir = '/var/sf/appid',
+ thirdparty_appid_dir = '/var/sf/appid/thirdparty_appid',
+ app_stats_filename = '/var/sf/appid/appid_stats.log',
+ app_stats_period = 3600,
+ app_stats_rollover_size = 100000000,
+ app_stats_rollover_time = 12,
+ instance_id = 222,
+ debug = true,
+ dump_ports = true,
+}
+
+
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// thirdparty_appid_api.h author Sourcefire Inc.
+
+#ifndef THIRDPARTY_APPID_API_H
+#define THIRDPARTY_APPID_API_H
+
+#include "protocols/packet.h"
+
+#include "appid_api.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 appid_tp_dir[TP_PATH_MAX];
+ int numXffFields;
+ char** xffFields;
+ int oldNumXffFields;
+ char** oldXffFields;
+};
+
+struct ThirdPartyUtils
+{
+ void (*logMsg)(const char*, ...);
+ uint32_t (*getSnortInstance)();
+};
+
+using ThirdPartyAppIDModInit = int(*)(ThirdPartyConfig*, ThirdPartyUtils*);
+using ThirdPartyAppIDModReconfigure = int(*)(ThirdPartyConfig*);
+using ThirdPartyAppIDModFini = int(*)();
+using ThirdPartyAppIDSessionCreate = void*(*)();
+using ThirdPartyAppIDSessionDelete = int(*)(void* tpsession, int just_reset_state);
+// FIXIT-L J use references for output parameters
+using ThirdPartyAppIDSessionProcess = int(*)(
+ void* tpsession, // in
+ Packet*, // in
+ int direction, // in
+ AppId*, // out
+ int* confidence, // out
+ AppId** proto_list, // out
+ ThirdPartyAppIDAttributeData** attribute_data // out
+);
+using ThirdPartyAppIDPrintStats = int(*)();
+using ThirdPartyAppIDResetStats = int(*)();
+using ThirdPartyAppIDDisableFlags = int(*)(void* tpsession, uint32_t session_flags);
+using ThirdPartyAppIDSessionStateGet = TPState(*)(void* tpsession);
+using ThirdPartyAppIDSessionStateSet = void(*)(void* tpsession, TPState);
+using ThirdPartyAppIDSessionAttrSet = void(*)(void* tpsession, TPSessionAttr);
+using ThirdPartyAppIDSessionAttrClear = void(*)(void* tpsession, TPSessionAttr);
+using ThirdPartyAppIDSessionAttrGet = unsigned(*)(void* tpsession, TPSessionAttr);
+using ThirdPartyAppIDSessionCurrenAppIdGet = AppId(*)(void* tpsession);
+
+// SO_PUBLIC const ThirdPartyAppIDModule thirdparty_appid_impl_module
+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;
+ ThirdPartyAppIDSessionCurrenAppIdGet session_appid_get;
+};
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// thirdparty_appid_types.h author Sourcefire Inc.
+
+#ifndef THIRDPARTY_APPID_TYPES_H
+#define THIRDPARTY_APPID_TYPES_H
+
+#include <stdint.h>
+
+// FIXIT-H J pulled from session_api.h include tree.
+// this belongs somewhere else...
+#define HTTP_MAX_XFF_FIELDS 255
+
+#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
+
+enum TPState
+{
+ TP_STATE_INIT,
+ TP_STATE_TERMINATED,
+ TP_STATE_INSPECTING,
+ TP_STATE_MONITORING,
+ TP_STATE_CLASSIFIED,
+ TP_STATE_HA = 21
+};
+
+enum TPSessionAttr
+{
+ 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),
+};
+
+struct XffFieldValue
+{
+ char* field;
+ char* value;
+};
+
+struct ThirdPartyAppIDAttributeData
+{
+ char* spdyRequestPath;
+ char* spdyRequestScheme;
+ char* spdyRequesHost;
+ char* httpRequesUrl;
+ char* httpRequestUri;
+ uint16_t httpRequestUriOffset;
+ uint16_t httpRequestUriEndOffset;
+ char* httpRequesHost;
+ char* httpRequestCookie;
+ uint16_t httpRequestCookieOffset;
+ uint16_t httpRequestCookieEndOffset;
+ char* httpRequestVia;
+ char* httpResponseVia;
+ char* httpResponseUpgrade;
+ char* httpRequestUserAgent;
+ char* httpResponseVersion;
+ char* httpResponseCode;
+ char* httpResponseContent;
+ char* httpResponseLocation;
+ char* httpResponseBody;
+ char* httpRequestBody;
+ char* httpResponseServer;
+ char* httpRequestXWorkingWith;
+ char* tlsHost;
+ char* tlsCname;
+ char* tlsOrgUnit;
+ char* httpRequestReferer;
+ char* ftpCommandUser;
+ XffFieldValue xffFieldValue[HTTP_MAX_XFF_FIELDS];
+ uint8_t numXffFields;
+};
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// thirdparty_appid_utils.cc author Sourcefire Inc.
+
+#include "thirdparty_appid_utils.h"
+
+#include <dlfcn.h>
+
+#include "main/snort_debug.h"
+#include "log/messages.h"
+#include "appid_config.h"
+#include "thirdparty_appid_api.h"
+
+#define MODULE_SYMBOL "thirdparty_appid_impl_module"
+
+THREAD_LOCAL void* module_handle = nullptr;
+THREAD_LOCAL struct ThirdPartyConfig thirdpartyConfig;
+THREAD_LOCAL ThirdPartyAppIDModule* thirdparty_appid_module = nullptr;
+
+// FIXIT - these need to be define or otherwise obtained...
+static char* defaultXffFields[] = { nullptr /* HTTP_XFF_FIELD_X_FORWARDED_FOR, */
+ /* HTTP_XFF_FIELD_TRUE_CLIENT_IP */ };
+
+int LoadCallback(const char* const path, int /* indent */)
+{
+ void* handle;
+ ThirdPartyAppIDModule* tp_module;
+
+ if (thirdparty_appid_module != nullptr)
+ {
+ ErrorMessage("Ignoring additional 3rd party AppID module (%s)!\n", path);
+ return 0;
+ }
+
+ handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
+ if (handle == nullptr)
+ {
+ ErrorMessage("Failed to load 3rd party AppID module: %s - %s\n", path, dlerror());
+ return 0;
+ }
+
+ tp_module = (ThirdPartyAppIDModule*)dlsym(handle, MODULE_SYMBOL);
+ if (tp_module == nullptr)
+ {
+ ErrorMessage("Failed to fine symbol %s in library %s\n", MODULE_SYMBOL, path);
+ dlclose(handle);
+ return 0;
+ }
+
+ if ( (tp_module->api_version != THIRD_PARTY_APP_ID_API_VERSION)
+ || ((tp_module->module_name == nullptr) || (tp_module->module_name[0] == 0))
+ || (tp_module->init == nullptr)
+ || (tp_module->fini == nullptr)
+ || (tp_module->session_create == nullptr)
+ || (tp_module->session_delete == nullptr)
+ || (tp_module->session_process == nullptr)
+ || (tp_module->print_stats == nullptr)
+ || (tp_module->reset_stats == nullptr)
+ || (tp_module->disable_flags == nullptr) )
+ {
+ ErrorMessage("Ignoring incomplete 3rd party AppID module (%s)!\n", path);
+ dlclose(handle);
+ return 0;
+ }
+
+ DEBUG_WRAP(DebugFormat(DEBUG_APPID, "Found 3rd party AppID module (%s).\n",
+ tp_module->module_name ? tp_module->module_name : ""); );
+ module_handle = handle;
+ thirdparty_appid_module = tp_module;
+ return 0;
+}
+
+void ThirdPartyAppIDInit(AppIdModuleConfig* appidStaticConfig)
+{
+ const char* thirdparty_appid_dir = appidStaticConfig->thirdparty_appid_dir;
+ int ret;
+ struct ThirdPartyUtils thirdpartyUtils;
+
+ if ( ( thirdparty_appid_module != nullptr ) || ( thirdparty_appid_dir == nullptr )
+ || ( thirdparty_appid_dir[0] == 0 ) )
+ return;
+
+ // FIXIT - need to port loadAllLibs function to snort3
+ // _dpd.loadAllLibs(thirdparty_appid_dir, LoadCallback);
+ if (thirdparty_appid_module == nullptr)
+ {
+ 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;
+ thirdpartyConfig.appid_tp_dir[0] = '\0'; // use default path
+
+ // FIXIT - need to provide log function and getSnortInstance function to 3rd party utils
+#ifdef REMOVED_WHILE_NOT_IN_USE
+ thirdpartyUtils.logMsg = &DebugFormat;
+ thirdpartyUtils.getSnortInstance = _dpd.getSnortInstance;
+
+ // FIXIT - need to get xff fields from http config
+ thirdpartyConfig.xffFields = _dpd.getHttpXffFields(&thirdpartyConfig.numXffFields);
+#endif
+
+ if (!thirdpartyConfig.xffFields)
+ {
+ thirdpartyConfig.xffFields = defaultXffFields;
+ thirdpartyConfig.numXffFields = sizeof(defaultXffFields) / sizeof(defaultXffFields[0]);
+ }
+
+ ret = thirdparty_appid_module->init(&thirdpartyConfig, &thirdpartyUtils);
+ if (ret != 0)
+ {
+ ErrorMessage("Unable to initialize 3rd party AppID module (%d)!\n", ret);
+ dlclose(module_handle);
+ module_handle = nullptr;
+ thirdparty_appid_module = nullptr;
+ return;
+ }
+
+ DEBUG_WRAP(DebugFormat(DEBUG_APPID,
+ "3rd party AppID module loaded and initialized OK (%s).\n",
+ thirdparty_appid_module->module_name ? thirdparty_appid_module->module_name : ""); );
+}
+
+void ThirdPartyAppIDReconfigure(void)
+{
+ int ret;
+
+ if (thirdparty_appid_module == nullptr)
+ {
+ DEBUG_WRAP(DebugMessage(DEBUG_APPID, "No 3rd party AppID module loaded.\n"); );
+ return;
+ }
+
+ thirdpartyConfig.oldNumXffFields = thirdpartyConfig.numXffFields;
+ thirdpartyConfig.oldXffFields = thirdpartyConfig.xffFields;
+
+ // FIXIT - need to get xff fields from http config
+ // thirdpartyConfig.xffFields = _dpd.getHttpXffFields(&thirdpartyConfig.numXffFields);
+ if (!thirdpartyConfig.xffFields)
+ {
+ thirdpartyConfig.xffFields = defaultXffFields;
+ thirdpartyConfig.numXffFields = sizeof(defaultXffFields) / sizeof(defaultXffFields[0]);
+ }
+
+ ret = thirdparty_appid_module->reconfigure(&thirdpartyConfig);
+ if (ret != 0)
+ {
+ ErrorMessage("Unable to reconfigure 3rd party AppID module (%d)!\n", ret);
+ return;
+ }
+
+ DEBUG_WRAP(DebugFormat(DEBUG_APPID, "3rd party AppID module reconfigured OK (%s).\n",
+ thirdparty_appid_module->module_name ? thirdparty_appid_module->module_name : ""); );
+}
+
+void ThirdPartyAppIDFini(void)
+{
+ int ret;
+
+ if (thirdparty_appid_module != nullptr)
+ {
+ ret = thirdparty_appid_module->fini();
+ if (ret != 0)
+ {
+ ErrorMessage("Could not finalize 3rd party AppID module (%d)!\n", ret);
+ }
+
+ dlclose(module_handle);
+ module_handle = nullptr;
+ thirdparty_appid_module = nullptr;
+
+ DEBUG_WRAP(DebugMessage(DEBUG_APPID,
+ "3rd party AppID module finalized and unloaded OK.\n"); );
+ }
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// thirdparty_appid_utils.h author Sourcefire Inc.
+
+#ifndef THIRDPARTY_APPID_UTILS_H
+#define THIRDPARTY_APPID_UTILS_H
+
+#include "main/thread.h"
+
+class AppIdModuleConfig;
+struct ThirdPartyAppIDModule;
+
+extern THREAD_LOCAL ThirdPartyAppIDModule* thirdparty_appid_module; // nullptr means no 3rd party AppID module
+
+void ThirdPartyAppIDInit(AppIdModuleConfig*);
+void ThirdPartyAppIDReconfigure();
+void ThirdPartyAppIDFini();
+
+#endif
+
--- /dev/null
+set ( UTIL_SOURCES
+ common_util.h
+ fw_avltree.cc
+ fw_avltree.h
+ ip_funcs.cc
+ ip_funcs.h
+ network_set.cc
+ network_set.h
+ output_file.cc
+ output_file.h
+ sfksearch.cc
+ sfksearch.h
+ sf_mlmp.cc
+ sf_mlmp.h
+ sf_multi_mpse.cc
+ sf_multi_mpse.h
+ sfutil.cc
+ sfutil.h
+ )
+
+add_library ( appid_util STATIC
+ ${UTIL_SOURCES}
+)
+
+target_include_directories ( appid_util PRIVATE ${APPID_INCLUDE_DIR} )
+
+# FIXIT-H: Add unit tests
+
+#install (FILES ${UTIL_INCLUDES}
+# DESTINATION "${INCLUDE_INSTALL_PATH}/appid/util"
+#)
--- /dev/null
+
+AM_CPPFLAGS+=-I$(top_srcdir)/src/network_inspectors/appid
+
+file_list = \
+common_util.h \
+fw_avltree.cc \
+fw_avltree.h \
+ip_funcs.cc \
+ip_funcs.h \
+network_set.cc \
+network_set.h \
+output_file.cc \
+output_file.h \
+sfksearch.cc \
+sfksearch.h \
+sf_mlmp.cc \
+sf_mlmp.h \
+sf_multi_mpse.cc \
+sf_multi_mpse.h \
+sfutil.cc \
+sfutil.h
+
+noinst_LIBRARIES = libappid_util.a
+libappid_util_a_SOURCES = $(file_list)
+
+# FIXIT - H Uncomment once tests are ready.
+#if BUILD_UNIT_TESTS
+#SUBDIRS = test
+#endif
\ No newline at end of file
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// common_util.h author Sourcefire Inc.
+
+#ifndef COMMON_UTIL_H
+#define COMMON_UTIL_H
+
+#include <cstdint>
+#include <cstdio>
+#include <ctime>
+#include <cctype>
+
+#include "framework/decode_data.h"
+
+struct FWDebugSessionConstraints
+{
+ ip::snort_in6_addr sip;
+ int sip_flag;
+ ip::snort_in6_addr dip;
+ int dip_flag;
+ uint16_t sport;
+ uint16_t dport;
+ PktType protocol;
+};
+
+#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)
+
+struct ConfigItem
+{
+ char* name; /* name of the config item */
+ char* value; /* config item value */
+};
+
+#define MAX_LINE 2048
+#define MAX_TOKS 256
+
+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<len; i++, pos++)
+ {
+ if (pos == 17)
+ {
+ str[pos] = 0;
+ fprintf(fp, " %s\n", str);
+ pos = 0;
+ }
+ else if (pos == 8)
+ {
+ str[pos] = ' ';
+ pos++;
+ fprintf(fp, "%s", " ");
+ }
+ c = (char)data[i];
+ if (isprint(c) && !isspace(c))
+ str[pos] = c;
+ else
+ str[pos] = '.';
+ fprintf(fp, "%02X ", data[i]);
+ }
+ if (pos)
+ {
+ str[pos] = 0;
+ for (; pos < 17; pos++)
+ {
+ if (pos == 8)
+ {
+ str[pos] = ' ';
+ pos++;
+ fprintf(fp, "%s", " ");
+ }
+ else
+ {
+ fprintf(fp, "%s", " ");
+ }
+ }
+ fprintf(fp, " %s\n", str);
+ }
+}
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// fw_avltree.cc author Sourcefire Inc.
+
+#include "fw_avltree.h"
+#include <string.h>
+#include <stdlib.h>
+
+#include "utils/util.h"
+
+static inline int is_root(FwAvlNode* node) { return (node->parent == nullptr); }
+static inline int get_balance(FwAvlNode* node) { return node->balance; }
+static inline void set_balance(int balance, FwAvlNode* node) { node->balance = balance; }
+static inline int inc_balance(FwAvlNode* node) { return ++node->balance; }
+static inline int dec_balance(FwAvlNode* node) { return --node->balance; }
+static inline FwAvlNode* get_parent(FwAvlNode* node) { return node->parent; }
+
+static inline void set_parent(FwAvlNode* parent, FwAvlNode* node)
+{
+ node->parent = parent;
+}
+
+static inline FwAvlNode* new_node(uint32_t key, void* data)
+{
+ FwAvlNode* node = (FwAvlNode*)snort_calloc(sizeof(FwAvlNode));
+
+ node->key = key;
+ node->data = data;
+ return node;
+}
+
+static inline FwAvlNode* get_first(FwAvlNode* node)
+{
+ while (node->left != nullptr)
+ node = node->left;
+ return node;
+}
+
+static inline FwAvlNode* get_last(FwAvlNode* node)
+{
+ while (node->right != nullptr)
+ node = node->right;
+ return node;
+}
+
+FwAvlNode* fwAvlFirst(const FwAvlTree* tree)
+{
+ if ((tree != 0) && (tree->root != 0))
+ return get_first(tree->root);
+ else
+ return nullptr;
+}
+
+FwAvlNode* fwAvlLast(const FwAvlTree* tree)
+{
+ if ((tree != 0) && (tree->root != 0))
+ return get_last(tree->root);
+ else
+ return nullptr;
+}
+
+FwAvlNode* fwAvlNext(FwAvlNode* node)
+{
+ FwAvlNode* parent = nullptr;
+ FwAvlNode* tmp;
+
+ if (node->right != nullptr)
+ {
+ return get_first(node->right);
+ }
+ else
+ {
+ tmp = node;
+ while ( ((parent = get_parent(tmp)) != nullptr) && (parent->right == tmp) )
+ tmp = parent;
+ }
+ return parent;
+}
+
+FwAvlNode* fwAvlPrev(FwAvlNode* node)
+{
+ FwAvlNode* parent;
+ FwAvlNode* tmp;
+
+ if (node->left != nullptr)
+ {
+ tmp = get_first(node->left);
+ }
+ else
+ {
+ tmp = node;
+ while ( ((parent = get_parent(tmp)) != nullptr) && (parent->left == tmp) )
+ tmp = parent;
+ }
+ return tmp;
+}
+
+static void rotate_left(FwAvlNode* node, FwAvlTree* tree)
+{
+ FwAvlNode* p = node;
+ FwAvlNode* q = node->right;
+ 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 != nullptr)
+ {
+ set_parent(p, p->right);
+ }
+ q->left = p;
+}
+
+static void rotate_right(FwAvlNode* node, FwAvlTree* tree)
+{
+ FwAvlNode* p = node;
+ FwAvlNode* q = node->left;
+ 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 != nullptr)
+ {
+ set_parent(p, p->left);
+ }
+ q->right = p;
+}
+
+static inline FwAvlNode* do_lookup(const uint32_t key,
+ const FwAvlTree* tree, FwAvlNode** pparent,
+ FwAvlNode** unbalanced, int* is_left)
+{
+ FwAvlNode* node = tree->root;
+
+ *pparent = nullptr;
+ *unbalanced = node;
+ *is_left = 0;
+
+ while (node != nullptr)
+ {
+ 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 nullptr;
+}
+
+void* fwAvlLookup(const uint32_t key, const FwAvlTree* tree)
+{
+ FwAvlNode* node = nullptr;
+ FwAvlNode* pparent;
+ FwAvlNode* unbalanced;
+ int is_left;
+
+ if (tree == 0)
+ {
+ return nullptr;
+ }
+
+ node = do_lookup(key, tree, &pparent, &unbalanced, &is_left);
+
+ if (node != nullptr)
+ {
+ return node->data;
+ }
+
+ return nullptr;
+}
+
+static inline void set_child(FwAvlNode* child, FwAvlNode* node,
+ int left)
+{
+ if (left != 0)
+ node->left = child;
+ else
+ node->right = child;
+}
+
+int fwAvlInsert(uint32_t key, void* data, FwAvlTree* tree)
+{
+ int is_left;
+ FwAvlNode* parent;
+ FwAvlNode* right;
+ FwAvlNode* left;
+ FwAvlNode* unbalanced;
+ FwAvlNode* node;
+
+ if (do_lookup(key, tree, &parent, &unbalanced, &is_left) != nullptr)
+ return 1;
+
+ if (!(node = new_node(key, data)))
+ return -1;
+
+ tree->count++;
+ if (parent == nullptr)
+ {
+ 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);
+
+ for (;; )
+ {
+ 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;
+}
+
+FwAvlTree* fwAvlInit()
+{
+ return (FwAvlTree*)snort_calloc(sizeof(FwAvlTree));
+}
+
+FwQNode* newFwQNode(FwAvlNode* treeNode)
+{
+ FwQNode* q_node = (FwQNode*)snort_calloc(sizeof(FwQNode));
+
+ q_node->treeNode = treeNode;
+ q_node->next = nullptr;
+ return(q_node);
+}
+
+FwQNode* fwAvlSerialize(FwAvlTree* tree)
+{
+ FwQNode* head;
+ FwQNode* node;
+ FwQNode* tail;
+
+ if ((tree == nullptr) || (tree->root == nullptr))
+ return nullptr;
+
+ head = newFwQNode(tree->root);
+ node = head;
+ tail = head;
+
+ while (node)
+ {
+ if (node->treeNode->left != nullptr)
+ {
+ tail->next = newFwQNode(node->treeNode->left);
+ tail = tail->next;
+ }
+
+ if (node->treeNode->right != nullptr)
+ {
+ tail->next = newFwQNode(node->treeNode->right);
+ tail = tail->next;
+ }
+
+ node = node->next;
+ }
+ return head;
+}
+
+void fwAvlDeleteTree(FwAvlTree* tree, void (* dataDelete)(void* data))
+{
+ FwQNode* node = fwAvlSerialize(tree);
+ FwQNode* tmp;
+
+ while (node != nullptr)
+ {
+ if (dataDelete)
+ dataDelete(node->treeNode->data);
+ snort_free(node->treeNode);
+ tmp = node;
+ node = node->next;
+ snort_free(tmp);
+ }
+ snort_free(tree);
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// fw_avltree.h author Sourcefire Inc.
+
+#ifndef FW_AVL_TREE_H
+#define FW_AVL_TREE_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+struct FwAvlNode
+{
+ uint32_t key;
+ void* data;
+ int balance;
+ FwAvlNode* left;
+ FwAvlNode* right;
+ FwAvlNode* parent;
+};
+
+struct FwAvlTree
+{
+ unsigned count;
+ size_t height;
+ FwAvlNode* root;
+ FwAvlNode* first;
+ FwAvlNode* last;
+};
+
+struct FwQNode
+{
+ FwAvlNode* treeNode;
+ FwQNode* next;
+};
+
+FwAvlTree* fwAvlInit();
+int fwAvlInsert(uint32_t key, void* data, FwAvlTree* tree);
+void* fwAvlLookup(const uint32_t key, const FwAvlTree* tree);
+FwAvlNode* fwAvlFirst(const FwAvlTree* tree);
+FwAvlNode* fwAvlLast(const FwAvlTree* tree);
+FwAvlNode* fwAvlNext(FwAvlNode* node);
+FwAvlNode* fwAvlPrev(FwAvlNode* node);
+FwQNode* fwAvlSerialize(FwAvlTree* tree);
+void fwAvlDeleteTree(FwAvlTree* tree, void (* dataDelete)(void* data));
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// ip_funcs.cc author Sourcefire Inc.
+
+#include "ip_funcs.h"
+
+#include "sfutil.h"
+#include "log/messages.h"
+#include "utils/util.h"
+
+RNAIpAddrSet* ParseIpCidr(char* ipstring, uint32_t* netmasks)
+{
+ char* toks[2];
+ int num_toks;
+ RNAIpAddrSet* ias;
+ char* cp;
+ struct in_addr ia;
+
+ if (ipstring == nullptr)
+ return nullptr;
+
+ ias = (RNAIpAddrSet*)snort_calloc(sizeof(RNAIpAddrSet));
+ 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)
+ {
+ ErrorMessage("IPFunctions: %s failed to translate", toks[0]);
+ snort_free(ias);
+ return nullptr;
+ }
+
+ ias->range_min = ntohl(ia.s_addr);
+
+ if (num_toks > 1)
+ {
+ ias->netmask = (unsigned)strtoul(toks[1], nullptr, 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 == nullptr)
+ return nullptr;
+
+ ias = (RNAIpv6AddrSet*)snort_calloc(sizeof(*ias));
+ 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)
+ {
+ ErrorMessage("IPFunctions: %s failed to translate", toks[0]);
+ snort_free(ias);
+ return nullptr;
+ }
+ memcpy(&ias->range_min, (const void*)&ia, sizeof(ias->range_min));
+ NSIPv6AddrNtoH(&ias->range_min);
+
+ if (num_toks > 1)
+ {
+ ias->netmask = (unsigned)strtoul(toks[1], nullptr, 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;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// ip_funcs.h author Sourcefire Inc.
+
+#ifndef IP_FUNCS_H
+#define IP_FUNCS_H
+
+#include <stdint.h>
+#include "protocols/ipv6.h"
+#include "network_set.h"
+
+// FIXIT - this must go when snort2.9.x sf_ip.h changes are ported to snort++
+#include "sfaddr_temp.h"
+
+#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
+
+struct RNAIpAddrSet
+{
+ uint32_t range_min;
+ uint32_t range_max;
+ uint32_t addr_flags;
+ unsigned netmask;
+ uint32_t netmask_mask;
+};
+
+RNAIpAddrSet* ParseIpCidr(char*, uint32_t*);
+
+struct RNAIpv6AddrSet
+{
+ NSIPv6Addr range_min;
+ NSIPv6Addr range_max;
+ uint32_t addr_flags;
+ unsigned netmask;
+ NSIPv6Addr netmask_mask;
+};
+
+RNAIpv6AddrSet* ParseIpv6Cidr(char*);
+
+inline void copyIpv4ToIpv6Network(ip::snort_in6_addr* keyIp, const uint32_t ip)
+{
+ keyIp->u6_addr32[0] = keyIp->u6_addr32[1] = 0;
+ keyIp->u6_addr16[4] = 0;
+ keyIp->u6_addr16[5] = 0xFFFF;
+ keyIp->u6_addr32[3] = ip;
+}
+
+//these functions are needed since snort does not store IPv4 address in highest 4 bytes
+//of 16 byte ip.
+inline void copySnortIpToIpv6Network(ip::snort_in6_addr* keyIp, const sfip_t* snortIp)
+{
+ memcpy(keyIp, snortIp->ip32, sizeof(*keyIp));
+}
+
+inline int cmpSnortIpToHostKey(const ip::snort_in6_addr* keyIp, const sfip_t* snortIp)
+{
+ return memcmp(keyIp, snortIp->ip32, sizeof(*keyIp));
+}
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// network_set.cc author Sourcefire Inc.
+
+#include "network_set.h"
+
+#include "log/messages.h"
+#include "utils/util.h"
+
+int NetworkSet_New(NetworkSet** network_set)
+{
+ NetworkSet* tmp = nullptr;
+
+ if (!network_set)
+ return -1;
+
+ tmp = (NetworkSet*)snort_calloc(sizeof(NetworkSet));
+ sflist_init(&tmp->networks);
+ tmp->ids = sfxhash_new(64, sizeof(unsigned), 0, 0, 0, nullptr, nullptr, 1);
+ if (tmp->ids == nullptr)
+ {
+ ErrorMessage("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, nullptr, nullptr, 1);
+ if (tmp->ids6 == nullptr)
+ {
+ ErrorMessage("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)
+ {
+ snort_free(network_set->pnetwork);
+ network_set->pnetwork = nullptr;
+ }
+ sflist_static_free_all(&network_set->networks, &snort_free);
+ sfxhash_delete(network_set->ids);
+ if (network_set->pnetwork6)
+ {
+ snort_free(network_set->pnetwork6);
+ network_set->pnetwork6 = nullptr;
+ }
+ sflist_static_free_all(&network_set->networks6, &snort_free);
+ sfxhash_delete(network_set->ids6);
+ snort_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;
+
+ network = (Network*)snort_calloc(sizeof(Network));
+ 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)
+ {
+ SF_LNODE* iter = nullptr;
+
+ for (iNetwork = (Network*)sflist_first(&network_set->networks, &iter);
+ iNetwork;
+ iNetwork = (Network*)sflist_next(&iter))
+ {
+ 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;
+ snort_free(network);
+ return 0;
+ }
+ }
+ }
+
+ if (sflist_add_tail(&network_set->networks, (void*)network))
+ {
+ ErrorMessage("NetworkSet:Out of memory");
+ snort_free(network);
+ return -1;
+ }
+
+ rval = sfxhash_add(network_set->ids, &network->info.id, &network->info.id);
+ if (rval != SFXHASH_OK && rval != SFXHASH_INTABLE)
+ {
+ ErrorMessage("NetworkSet:Out of memory");
+ snort_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;
+
+ network = (Network6*)snort_calloc(sizeof(Network6));
+ 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)
+ {
+ SF_LNODE* iter = nullptr;
+
+ for (iNetwork = (Network6*)sflist_first(&network_set->networks6, &iter);
+ iNetwork;
+ iNetwork = (Network6*)sflist_next(&iter))
+ {
+ 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;
+ snort_free(network);
+ return 0;
+ }
+ }
+ }
+
+ // FIXIT - these sflist funcs probably should never fail now either since they use snort_calloc...
+ if (sflist_add_tail(&network_set->networks6, (void*)network))
+ {
+ ErrorMessage("NetworkSet:Out of memory");
+ snort_free(network);
+ return -1;
+ }
+ rval = sfxhash_add(network_set->ids6, &network->info.id, &network->info.id);
+ if (rval != SFXHASH_OK && rval != SFXHASH_INTABLE)
+ {
+ ErrorMessage("NetworkSet:Out of memory");
+ snort_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;
+ SF_LNODE* iter = nullptr;
+ char min_ip[INET6_ADDRSTRLEN];
+ char max_ip[INET6_ADDRSTRLEN];
+
+ for (network = (Network*)sflist_first(&network_set->networks, &iter);
+ network;
+ network = (Network*)sflist_next(&iter))
+ {
+ 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, &iter);
+ network6;
+ network6 = (Network6*)sflist_next(&iter))
+ {
+ 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;
+}
+
+NODE_DATA sflist_first(SF_LIST*, SF_LNODE**);
+NODE_DATA sflist_next(SF_LNODE**);
+static inline int NetworkSet_OrderByNetmask(SF_LIST* ordered_networks, SF_LIST* networks, unsigned
+ id)
+{
+ SF_LNODE* node = nullptr;
+ NODE_DATA node_data;
+ NSNetworkInfo* network;
+
+ sflist_init(ordered_networks);
+ do
+ {
+ SF_LNODE* iter = nullptr;
+
+ node_data = nullptr;
+ for (network = (NSNetworkInfo*)sflist_first(networks, &iter);
+ network;
+ network = (NSNetworkInfo*)sflist_next(&iter))
+ {
+ if ( network->id == id && (node_data == nullptr ||
+ network->netmask < ((NSNetworkInfo*)node_data)->netmask ||
+ ( ( network->netmask == ((NSNetworkInfo*)node_data)->netmask) &&
+ !network->ip_not ) ) )
+ {
+ node_data = network;
+ node = iter;
+ }
+ }
+
+ if (node_data)
+ {
+ if (sflist_add_tail(ordered_networks, node_data) )
+ return -1;
+
+ sflist_remove_node(networks, node);
+ }
+ }
+ while (node_data);
+
+ 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;
+ bool changed;
+ SF_LIST reduced_networks;
+
+ if (!sflist_count(networks))
+ return 0;
+
+ sflist_init(&reduced_networks);
+ while ( ( ias = (Network*)sflist_remove_head(networks) ) )
+ {
+ SF_LNODE* iter = nullptr;
+
+ /* ias is lowest in the list, so it takes precedence */
+ if (ias->info.ip_not)
+ {
+ i_ias = (Network*)sflist_first(&reduced_networks, &iter);
+ while (i_ias)
+ {
+ changed = false;
+
+ /*
+ i_ias ******
+ ias ***************
+ */
+ if (ias->range_min <= i_ias->range_min && ias->range_max >= i_ias->range_max)
+ {
+ sflist_remove_node(&reduced_networks, iter);
+ changed = true;
+ }
+ /*
+ 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)
+ {
+ new_ias = (Network*)snort_calloc(sizeof(Network));
+ *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))
+ {
+ snort_free(new_ias);
+ return -1;
+ }
+ changed = true;
+ }
+ }
+ /*
+ 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)
+ {
+ new_ias = (Network*)snort_calloc(sizeof(Network));
+ *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))
+ {
+ snort_free(new_ias);
+ return -1;
+ }
+ changed = true;
+ }
+ }
+
+ if (changed)
+ i_ias = (Network*)sflist_first(&reduced_networks, &iter);
+ else
+ i_ias = (Network*)sflist_next(&iter);
+ }
+
+ snort_free(ias);
+ }
+ else
+ {
+ i_ias = (Network*)sflist_first(&reduced_networks, &iter);
+ while (i_ias)
+ {
+ changed = false;
+ 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, iter);
+ changed = true;
+ snort_free(i_ias);
+ i_ias = nullptr;
+ }
+ /*
+ 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, iter);
+ changed = true;
+ snort_free(i_ias);
+ i_ias = nullptr;
+ }
+ /*
+ 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;
+ snort_free(ias);
+ ias = nullptr;
+ 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, iter);
+ snort_free(i_ias);
+ i_ias = nullptr;
+ changed = true;
+ }
+ /*
+ 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)
+ {
+ new_ias = (Network*)snort_calloc(sizeof(Network));
+ *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))
+ {
+ snort_free(new_ias);
+ return -1;
+ }
+ changed = true;
+ }
+ }
+ /*
+ 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;
+ }
+ }
+
+ if (changed)
+ i_ias = (Network*)sflist_first(&reduced_networks, &iter);
+ else
+ i_ias = (Network*)sflist_next(&iter);
+ }
+
+ if (ias && sflist_add_tail(&reduced_networks, ias))
+ {
+ return -1;
+ }
+ }
+ }
+
+ /* Minimize the ranges */
+ SF_LNODE* outer_iter;
+ ias = (Network*)sflist_first(&reduced_networks, &outer_iter);
+ while (ias)
+ {
+ /* i_ias is lowest in the list, so it takes precedence */
+ changed = false;
+ SF_LNODE* inner_iter = outer_iter;
+
+ i_ias = (Network*)sflist_next(&inner_iter);
+ while ( i_ias )
+ {
+ 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, outer_iter);
+ snort_free(ias);
+ changed = true;
+ 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, outer_iter);
+ snort_free(ias);
+ changed = true;
+ break;
+ }
+ }
+
+ i_ias = (Network*)sflist_next(&inner_iter);
+ }
+
+ if (changed)
+ ias = (Network*)sflist_first(&reduced_networks, &outer_iter);
+ else
+ ias = (Network*)sflist_next(&outer_iter);
+ }
+
+ sflist_static_free_all(networks, &snort_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;
+ bool changed;
+ SF_LIST reduced_networks;
+
+ if (!sflist_count(networks))
+ return 0;
+
+ sflist_init(&reduced_networks);
+ while ((ias = (Network6*)sflist_remove_head(networks)))
+ {
+ SF_LNODE* iter = nullptr;
+
+ /* ias is lowest in the list, so it takes precedence */
+ if (ias->info.ip_not)
+ {
+ i_ias = (Network6*)sflist_first(&reduced_networks, &iter);
+ while (i_ias)
+ {
+ changed = false;
+
+ /*
+ 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, iter);
+ changed = true;
+ }
+ /*
+ 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)
+ {
+ new_ias = (Network6*)snort_calloc(sizeof(Network6));
+ *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))
+ {
+ snort_free(new_ias);
+ return -1;
+ }
+ changed = true;
+ }
+ }
+ /*
+ 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)
+ {
+ new_ias = (Network6*)snort_calloc(sizeof(Network6));
+ *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))
+ {
+ snort_free(new_ias);
+ return -1;
+ }
+ changed = true;
+ }
+ }
+
+ if (changed)
+ i_ias = (Network6*)sflist_first(&reduced_networks, &iter);
+ else
+ i_ias = (Network6*)sflist_next(&iter);
+ }
+ snort_free(ias);
+ }
+ else
+ {
+ i_ias = (Network6*)sflist_first(&reduced_networks, &iter);
+ while (i_ias)
+ {
+ changed = false;
+ 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, iter);
+ changed = true;
+ snort_free(i_ias);
+ i_ias = nullptr;
+ }
+ /*
+ 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, iter);
+ changed = true;
+ snort_free(i_ias);
+ i_ias = nullptr;
+ }
+ /*
+ 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;
+ snort_free(ias);
+ ias = nullptr;
+ 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, iter);
+ snort_free(i_ias);
+ i_ias = nullptr;
+ changed = true;
+ }
+ /*
+ 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)
+ {
+ new_ias = (Network6*)snort_calloc(sizeof(Network6));
+ *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))
+ {
+ snort_free(new_ias);
+ return -1;
+ }
+ changed = true;
+ }
+ }
+ /*
+ 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);
+ }
+ }
+
+ if (changed)
+ i_ias = (Network6*)sflist_first(&reduced_networks, &iter);
+ else
+ i_ias = (Network6*)sflist_next(&iter);
+ }
+
+ if (ias && sflist_add_tail(&reduced_networks, ias))
+ {
+ return -1;
+ }
+ }
+ }
+
+ /* Minimize the ranges */
+ SF_LNODE* outer_iter;
+ ias = (Network6*)sflist_first(&reduced_networks, &outer_iter);
+ while (ias)
+ {
+ /* i_ias is lowest in the list, so it takes precedence */
+ changed = false;
+ SF_LNODE* inner_iter = outer_iter;
+ i_ias = (Network6*)sflist_next(&inner_iter);
+ while ( i_ias )
+ {
+ 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, outer_iter);
+ snort_free(ias);
+ changed = true;
+ 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, outer_iter);
+ snort_free(ias);
+ changed = true;
+ break;
+ }
+ }
+
+ i_ias = (Network6*)sflist_next(&inner_iter);
+ }
+
+ if (changed)
+ ias = (Network6*)sflist_first(&reduced_networks, &outer_iter);
+ else
+ ias = (Network6*)sflist_next(&outer_iter);
+ }
+
+ sflist_static_free_all(networks, &snort_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, &snort_free);
+ return rval;
+ }
+ if ((rval = NetworkSet_ReduceNetworkSet(&ordered_networks)) != 0)
+ {
+ sflist_free_all(&ordered_networks, &snort_free);
+ return rval;
+ }
+ if ((rval = NetworkSet_AddList(&network_set->networks, &ordered_networks)) != 0)
+ {
+ sflist_free_all(&ordered_networks, &snort_free);
+ return rval;
+ }
+ }
+ if ((rval = NetworkSet_ReduceNetworkSet(&network_set->networks)) != 0)
+ {
+ sflist_free_all(&ordered_networks, &snort_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)
+ {
+ snort_free(network_set->pnetwork);
+ network_set->pnetwork = nullptr;
+ }
+ network_set->pnetwork = (Network**)snort_calloc(count * sizeof(Network*));
+ SF_LNODE* iter = nullptr;
+ for (network = (Network*)sflist_first(&network_set->networks, &iter), i = 0;
+ network && i < count;
+ network = (Network*)sflist_next(&iter))
+ {
+ 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, &snort_free);
+ return rval;
+ }
+ if ((rval = NetworkSet_ReduceNetworkSet6(&ordered_networks)) != 0)
+ {
+ sflist_free_all(&ordered_networks, &snort_free);
+ return rval;
+ }
+ if ((rval = NetworkSet_AddList(&network_set->networks6, &ordered_networks)) != 0)
+ {
+ sflist_free_all(&ordered_networks, &snort_free);
+ return rval;
+ }
+ }
+ if ((rval = NetworkSet_ReduceNetworkSet6(&network_set->networks6)) != 0)
+ {
+ sflist_free_all(&ordered_networks, &snort_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)
+ {
+ snort_free(network_set->pnetwork6);
+ network_set->pnetwork6 = nullptr;
+ }
+ network_set->pnetwork6 = (Network6**)snort_calloc(count * sizeof(Network6*));
+ SF_LNODE* iter = nullptr;
+ for (network6 = (Network6*)sflist_first(&network_set->networks6, &iter), i = 0;
+ network6 && i < count;
+ network6 = (Network6*)sflist_next(&iter))
+ {
+ 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;
+ SF_LNODE* iter;
+
+ if (!network_set)
+ return nullptr;
+
+ if (NetworkSet_New(&new_set) != 0)
+ return nullptr;
+
+ for (network = (Network*)sflist_first(&network_set->networks, &iter);
+ network;
+ network = (Network*)sflist_next(&iter))
+ {
+ 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 nullptr;
+ }
+ }
+ for (network6 = (Network6*)sflist_first(&network_set->networks6, &iter);
+ network6;
+ network6 = (Network6*)sflist_next(&iter))
+ {
+ 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 nullptr;
+ }
+ }
+ return new_set;
+}
+
+int NetworkSet_AddSet(NetworkSet* dest_set, NetworkSet* src_set)
+{
+ Network* network;
+ Network6* network6;
+ SF_LNODE* iter;
+ int rval;
+
+ if (!src_set || !dest_set)
+ return -1;
+
+ for (network = (Network*)sflist_first(&src_set->networks, &iter);
+ network;
+ network = (Network*)sflist_next(&iter))
+ {
+ 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, &iter);
+ network6;
+ network6 = (Network6*)sflist_next(&iter))
+ {
+ 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;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// network_set.h author Sourcefire Inc.
+
+#ifndef NETWORK_SET_H
+#define NETWORK_SET_H
+
+/* System includes */
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <string.h>
+#include <limits.h>
+
+#include "utils/sflsq.h"
+#include "hash/sfxhash.h"
+#include "protocols/ipv6.h"
+
+// network_set.h author Sourcefire Inc.
+
+#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)
+struct NSIPv6Addr
+{
+ uint64_t hi;
+ uint64_t lo;
+};
+#else
+struct NSIPv6Addr
+{
+ uint64_t lo;
+ uint64_t hi;
+};
+#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)))
+
+inline void NSIPv6PackIpv4(NSIPv6Addr* ipv6Addr, uint32_t ipv4Addr)
+{
+ ipv6Addr->hi = 0ULL;
+ ipv6Addr->lo = (uint64_t)ipv4Addr | 0x0000FFFF00000000ULL;
+}
+
+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;
+}
+
+inline void NSIPv6AddrCopy(const NSIPv6Addr* src, NSIPv6Addr* dst)
+{
+ dst->hi = src->hi;
+ dst->lo = src->lo;
+}
+
+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
+
+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)
+
+inline void _NSIPv6AddrConv(const NSIPv6Addr* ip6, NSIPv6Addr* ip6h)
+{
+ ip6h->hi = ip6->hi;
+ ip6h->lo = ip6->lo;
+}
+
+#else
+
+inline void _NSIPv6AddrConv(const NSIPv6Addr* ip6, NSIPv6Addr* ip6h)
+{
+ ip6h->hi = BYTE_SWAP_64(ip6->lo);
+ ip6h->lo = BYTE_SWAP_64(ip6->hi);
+}
+
+#endif
+
+inline void NSIPv6AddrNtoHConv(const ip::snort_in6_addr* ip6, NSIPv6Addr* ip6h)
+{
+ _NSIPv6AddrConv((const NSIPv6Addr*)ip6, ip6h);
+}
+
+inline void NSIPv6AddrHtoNConv(const NSIPv6Addr* ip6, ip::snort_in6_addr* ip6h)
+{
+ _NSIPv6AddrConv(ip6, (NSIPv6Addr*)ip6h);
+}
+
+#define NSIPv6AddrHtoN(ip6) NSIPv6AddrNtoH(ip6)
+
+inline void NSIPv6AddrInc(NSIPv6Addr* ip6)
+{
+ if (ip6->lo == ULLONG_MAX)
+ {
+ ip6->lo = 0;
+ ip6->hi++;
+ }
+ else
+ ip6->lo++;
+}
+
+inline void NSIPv6AddrDec(NSIPv6Addr* ip6)
+{
+ if (!ip6->lo)
+ {
+ ip6->lo = ULLONG_MAX;
+ ip6->hi--;
+ }
+ else
+ ip6->lo--;
+}
+
+struct NSNetworkInfo
+{
+ unsigned id;
+ unsigned netmask;
+ int ip_not;
+ unsigned type;
+};
+
+struct Network
+{
+ NSNetworkInfo info;
+ uint32_t range_min;
+ uint32_t range_max;
+};
+
+struct Network6
+{
+ NSNetworkInfo info;
+ NSIPv6Addr range_min;
+ NSIPv6Addr range_max;
+};
+
+struct NetworkSet
+{
+ NetworkSet* next;
+ SF_LIST networks;
+ SFXHASH* ids;
+ Network** pnetwork;
+ unsigned count;
+ SF_LIST networks6;
+ SFXHASH* ids6;
+ Network6** pnetwork6;
+ unsigned count6;
+};
+
+// Create a new network set
+int NetworkSet_New(NetworkSet** network_set);
+
+// Destroy a network set
+int NetworkSet_Destroy(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(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(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(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(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(NetworkSet* network_set);
+
+// Print the network to the specified stream
+int NetworkSet_Fprintf(NetworkSet* network_set, const char* prefix, FILE* stream);
+
+// Test is the set contains the specied address
+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
+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
+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
+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
+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
+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
+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
+inline unsigned NetworkSet_Count6Ex(NetworkSet* network_set)
+{
+ if (!network_set)
+ return 0;
+
+ return sflist_count(&network_set->networks6);
+}
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// output_file.cc author Sourcefire Inc.
+
+#include "output_file.h"
+
+#include <errno.h>
+#include <string.h>
+#include "log/messages.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(nullptr);
+ snprintf(output_fullpath, sizeof(output_fullpath), "%s.%lu", filename, curr_time);
+ LogMessage("*** Opening %s for output\n",output_fullpath);
+ if ((fp = fopen(output_fullpath, "w")) == nullptr)
+ {
+ ErrorMessage("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);
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// output_file.h author Sourcefire Inc.
+
+#ifndef OUTPUT_FILE
+#define OUTPUT_FILE
+
+#include <stdio.h>
+#include <time.h>
+
+FILE* openOutputFile(const char* const filename, time_t tstamp);
+FILE* rolloverOutputFile(const char* const filename, FILE* const oldfp, time_t tstamp);
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// sf_mlmp.cc author Sourcefire Inc.
+
+#include "sf_mlmp.h"
+
+#include <string.h>
+
+#include "main/snort_debug.h"
+#include "search_engines/search_tool.h"
+#include "utils/util.h"
+
+#define _MLMP_DEBUG 0
+
+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;
+
+ tPatternNode* nextPattern;
+};
+
+struct tPatternPrimaryNode
+{
+ tPatternNode patternNode;
+
+ tPatternPrimaryNode* nextPrimaryNode;
+
+ /*Tree node for next level. Present only in primary pattern node i.e. */
+ tMlmpTree* nextLevelMatcher;
+};
+
+/*Node for mlmp tree */
+struct tMlmpTree
+{
+ SearchTool* patternTree;
+ tPatternPrimaryNode* patternList;
+ uint32_t level;
+};
+
+/*Used to track matched patterns. */
+struct tMatchedPatternList
+{
+ tPatternNode* patternNode;
+ size_t index;
+ /*uint32_t level; */
+ tMatchedPatternList* next;
+};
+
+static int compareMlmpPatterns(const void* p1, const void* p2);
+static int createTreesRecusively(tMlmpTree* root);
+static void destroyTreesRecursively(tMlmpTree* root);
+static void dumpTreesRecursively(tMlmpTree* root);
+static int addPatternRecursively(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(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;
+
+tMlmpTree* mlmpCreate(void)
+{
+ tMlmpTree* root = (tMlmpTree*)snort_calloc(sizeof(tMlmpTree));
+ root->level = 0;
+ return root;
+}
+
+/*last pattern should be nullptr */
+int mlmpAddPattern(tMlmpTree* root, const tMlmpPattern* inputPatternList, void* metaData)
+{
+ return addPatternRecursively(root, inputPatternList, metaData, 0);
+}
+
+int mlmpProcessPatterns(tMlmpTree* root)
+{
+ int rvalue;
+
+ rvalue = createTreesRecusively(root);
+ if (rvalue)
+ destroyTreesRecursively(root);
+ return rvalue;
+}
+
+void* mlmpMatchPatternUrl(tMlmpTree* root, tMlmpPattern* inputPatternList)
+{
+ return mlmpMatchPatternCustom(root, inputPatternList, urlPatternSelector);
+}
+
+void* mlmpMatchPatternGeneric(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(tMlmpTree* rootNode, tMlmpPattern* inputPatternList,
+ tPatternNode* (*callback)(const tMatchedPatternList*, const uint8_t*))
+{
+ tMatchedPatternList* mp = nullptr;
+ tMatchedPatternList* tmpMp;
+ void* data = nullptr;
+ void* tmpData = nullptr;
+ tPatternPrimaryNode* primaryNode;
+ tMlmpPattern* pattern = inputPatternList;
+
+ if (!rootNode || !pattern || !pattern->pattern)
+ return nullptr;
+
+ rootNode->patternTree->find_all((char*)pattern->pattern, pattern->patternSize,
+ patternMatcherCallback, false, (void*)&mp);
+
+ primaryNode = (tPatternPrimaryNode*)callback(mp, pattern->pattern);
+
+ while (mp)
+ {
+ tmpMp = mp;
+ mp = mp->next;
+ snort_free(tmpMp);
+ }
+
+ if (primaryNode)
+ {
+ data = primaryNode->patternNode.userData;
+ tmpData = mlmpMatchPatternCustom(primaryNode->nextLevelMatcher, ++inputPatternList,
+ callback);
+ if (tmpData)
+ data = tmpData;
+ }
+
+ return data;
+}
+
+void mlmpDestroy(tMlmpTree* root)
+{
+ destroyTreesRecursively(root);
+}
+
+void mlmpDump(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(tMlmpTree* rootNode)
+{
+ SearchTool* patternMatcher;
+ tPatternPrimaryNode* primaryPatternNode;
+ tPatternNode* ddPatternNode;
+
+ /* set up the MPSE for url patterns */
+ patternMatcher = rootNode->patternTree = new SearchTool("ac_full");
+
+ 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)
+ {
+ patternMatcher->add(ddPatternNode->pattern.pattern,
+ ddPatternNode->pattern.patternSize, ddPatternNode, true);
+ }
+ }
+
+ patternMatcher->prep();
+
+ return 0;
+}
+
+static void destroyTreesRecursively(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);
+ snort_free((void*)patternNode->pattern.pattern);
+ }
+ snort_free(primaryPatternNode->patternNode.nextPattern);
+ snort_free((void*)primaryPatternNode->patternNode.pattern.pattern);
+ snort_free(primaryPatternNode);
+ }
+
+ delete rootNode->patternTree;
+ snort_free(rootNode);
+}
+
+static void dumpTreesRecursively(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)
+ {
+ DebugFormat(DEBUG_INSPECTOR, "%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)
+ {
+ DebugFormat(DEBUG_INSPECTOR, "%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 <patternId, partNum>.
+ 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 = nullptr;
+ tPatternNode* currentPrimaryNode = nullptr;
+ const tMatchedPatternList* tmpList;
+ uint32_t partNum, patternId, patternSize, maxPatternSize;
+
+ /*partTotal = 0; */
+ partNum = 0;
+ patternId = 0;
+ patternSize = maxPatternSize = 0;
+
+#if _MLMP_DEBUG
+ tPatternNode* ddPatternNode;
+ DebugMessage(DEBUG_INSPECTOR, "\tMatches found -------------------\n"); for (tmpList =
+ patternMatchList;
+ tmpList;
+ tmpList = tmpList->next)
+ {
+ ddPatternNode = tmpList->patternNode;
+ {
+ DebugFormat(DEBUG_INSPECTOR,
+ "\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;
+ {
+ DebugFormat(DEBUG_INSPECTOR,
+ "\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);
+ }
+ }
+ DebugMessage(DEBUG_INSPECTOR, "\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*, int index, void* data, void*)
+{
+ 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
+ DebugFormat(DEBUG_INSPECTOR,
+ "\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 = nullptr, tmpList = *matchList;
+ tmpList;
+ prevNode = tmpList, tmpList = tmpList->next)
+ {
+ cmp = compareMlmpPatternList (target, tmpList->patternNode);
+ if (cmp > 0 )
+ continue;
+ if (cmp == 0)
+ return 0;
+ break;
+ }
+
+ newNode = (tMatchedPatternList*)snort_calloc(sizeof(tMatchedPatternList));
+ newNode->index = index;
+ newNode->patternNode = target;
+
+ if (prevNode == nullptr)
+ {
+ /*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 nullptr means */
+static tPatternPrimaryNode* findMatchPattern(tMlmpTree* rootNode, const
+ tMlmpPattern* inputPatternList, uint32_t partTotal,
+ tPatternPrimaryNode** prevPrimaryPatternNode)
+{
+ tPatternPrimaryNode* primaryPatternNode;
+ tPatternNode* ddPatternNode;
+ uint32_t partNum;
+ int retVal;
+
+ *prevPrimaryPatternNode = nullptr;
+
+ 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 nullptr;
+ }
+ break;
+ }
+ /**prevPrimaryPatternNode = primaryPatternNode; */
+ }
+ return nullptr;
+}
+
+/**
+ * @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(tMlmpTree* rootNode, const tMlmpPattern* inputPatternList,
+ void* metaData, uint32_t level)
+{
+ tPatternNode* newNode;
+ tPatternPrimaryNode* prevPrimaryPatternNode = nullptr;
+ tPatternPrimaryNode* primaryNode = nullptr;
+ 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*)snort_calloc(sizeof(tPatternPrimaryNode));
+ if (partTotal > 1)
+ tmpPrimaryNode->patternNode.nextPattern =
+ (tPatternNode*)snort_calloc((partTotal - 1) * sizeof(tPatternNode));
+ 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 = nullptr;
+
+ i++;
+ patterns = inputPatternList + i;
+ }
+ primaryNode = tmpPrimaryNode;
+ }
+ else
+ {
+ for (i = 0; i < primaryNode->patternNode.partTotal; i++)
+ snort_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)
+ {
+ tMlmpTree* tmpRootNode;
+
+ tmpRootNode = (tMlmpTree*)snort_calloc(sizeof(tMlmpTree));
+ primaryNode->nextLevelMatcher = tmpRootNode;
+ primaryNode->nextLevelMatcher->level = rootNode->level+1;
+ }
+ addPatternRecursively(primaryNode->nextLevelMatcher, inputPatternList+partTotal,
+ metaData, level+1);
+ }
+ }
+
+ return 0;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// sf_mlmp.h author Sourcefire Inc.
+
+#ifndef SF_MULTI_PART_MPSE_H
+#define SF_MULTI_PART_MPSE_H
+
+#include <stdlib.h>
+#include <stdint.h>
+
+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;
+};
+
+struct tMlmpTree;
+
+tMlmpTree* mlmpCreate();
+int mlmpAddPattern(tMlmpTree* root, const tMlmpPattern* patterns, void* metaData);
+int mlmpProcessPatterns(tMlmpTree* root);
+void* mlmpMatchPatternUrl(tMlmpTree* root, tMlmpPattern* inputPatternList);
+void* mlmpMatchPatternGeneric(tMlmpTree* root, tMlmpPattern* inputPatternList);
+void mlmpDestroy(tMlmpTree* root);
+void mlmpDump(tMlmpTree* root);
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// sf_multi_mpse.cc author Sourcefire Inc.
+
+#include "sf_multi_mpse.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "search_engines/search_tool.h"
+#include "utils/util.h"
+
+struct tPatternRootNode;
+struct tPatternList
+{
+ tMlpPattern pattern;
+ void* userData; /*client/service info */
+
+ tPatternList* nextPattern;
+ tPatternRootNode* nextLevelMatcher;
+};
+
+/*Root node */
+struct tPatternRootNode
+{
+ SearchTool* patternTree;
+ tPatternList* patternList;
+ tPatternList* lastPattern;
+ unsigned int level; /*some searches may be specific to levels. Increments from 1 at top
+ level, */
+};
+
+/*Used to track matched patterns. */
+struct MatchedPattern
+{
+ tPatternList* patternNode;
+ size_t index;
+ unsigned int level;
+};
+
+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*, int index, void* data,
+ void*);
+static int url_pattern_match(void* id, void*, int index, void* data, void*);
+
+void* mlpCreate(void)
+{
+ tPatternRootNode* root = (tPatternRootNode*)snort_calloc(sizeof(tPatternRootNode));
+ root->level = 0;
+ return root;
+}
+
+/*last pattern should be nullptr */
+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 = { nullptr,0,0 };
+ void* data = nullptr;
+ void* tmpData = nullptr;
+ tPatternList* patternNode;
+ tPatternRootNode* rootNode = (tPatternRootNode*)root;
+ tMlpPattern* pattern = *inputPatternList;
+
+ if (!rootNode || !pattern || !pattern->pattern)
+ return nullptr;
+
+ mp.level = rootNode->level;
+
+ rootNode->patternTree->find_all((char*)pattern->pattern,
+ pattern->patternSize,
+ callback,
+ false,
+ &mp);
+
+ patternNode = mp.patternNode;
+ if (patternNode)
+ {
+ if (matchDomainPattern(mp, pattern->pattern) != 0)
+ return nullptr;
+
+ 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;
+ SearchTool* patternMatcher;
+ tPatternList* patternNode;
+
+ /* set up the MPSE for url patterns */
+ if (!(patternMatcher = rootNode->patternTree = new SearchTool("ac_full")))
+ return -1;
+
+ for (patternNode = rootNode->patternList;
+ patternNode;
+ patternNode = patternNode->nextPattern)
+ {
+ /*recursion into next lower level */
+ if (patternNode->nextLevelMatcher)
+ {
+ if (createTreesRecusively(patternNode->nextLevelMatcher))
+ return -1;
+ }
+
+ patternMatcher->add(patternNode->pattern.pattern,
+ patternNode->pattern.patternSize,
+ patternNode,
+ false);
+ }
+
+ patternMatcher->prep();
+
+ 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;
+ snort_free(patternNode);
+ }
+
+ delete rootNode->patternTree;
+ snort_free(rootNode);
+}
+
+static void dumpTreesRecursively(void* root, int level)
+{
+ tPatternRootNode* rootNode = (tPatternRootNode*)root;
+ tPatternList* patternNode;
+ char* offset;
+
+ offset = (char*)snort_calloc(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));
+ }
+ }
+ snort_free(offset);
+}
+
+static int longest_pattern_match(void* id, void*, int index, void* data,
+ void*)
+{
+ 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*, int index, void* data, void*)
+{
+ 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 = nullptr;
+ 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*)snort_calloc(sizeof(tPatternList));
+ newNode->pattern.pattern = patterns->pattern;
+ newNode->pattern.patternSize = patterns->patternSize;
+ newNode->nextLevelMatcher = (tPatternRootNode*)snort_calloc(sizeof(tPatternRootNode));
+ 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 = { nullptr,0,0 };
+ tPatternList* patternNode;
+ tPatternRootNode* rootNode = (tPatternRootNode*)root;
+ tMlpPattern* pattern = *inputPatternList;
+
+ if (!rootNode || !pattern || !pattern->pattern)
+ return nullptr;
+
+ mp.level = rootNode->level;
+
+ rootNode->patternTree->find_all((char*)pattern->pattern,
+ pattern->patternSize,
+ longest_pattern_match,
+ false,
+ &mp);
+
+ patternNode = mp.patternNode;
+ if (patternNode)
+ {
+ ++inputPatternList;
+ if (*inputPatternList && (*inputPatternList)->pattern)
+ {
+ return mlpMatchPatternCustom(patternNode->nextLevelMatcher, inputPatternList,
+ longest_pattern_match);
+ }
+ return patternNode->nextLevelMatcher;
+ }
+
+ return nullptr;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// sf_multi_mpse.h author Sourcefire Inc.
+
+#ifndef SF_MULTI_MPSE_H
+#define SF_MULTI_MPSE_H
+
+#include <stdlib.h>
+#include <stdint.h>
+
+struct tMlpPattern
+{
+ const uint8_t* pattern;
+ size_t patternSize;
+};
+
+void* mlpCreate();
+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
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// sfksearch.cc author Sourcefire Inc.
+
+/*
+*
+* 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.
+*
+*/
+
+#include "sfksearch.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <ctype.h>
+
+#include "utils/util.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 nullptr;
+
+ p = snort_calloc(n);
+ mtot += n;
+
+ return p;
+}
+
+/*
+* Free Memory
+*/
+static void KTRIE_FREE(void* p)
+{
+ if (p == nullptr)
+ return;
+
+ snort_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, const 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 = nullptr;
+ KTRIEPATTERN* pnext = nullptr;
+ int i;
+
+ if (k == nullptr)
+ return;
+
+ p = k->patrn;
+
+ while (p != nullptr)
+ {
+ 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 == nullptr)
+ return;
+
+ KTrieFree(n->child);
+ KTrieFree(n->sibling);
+
+ KTRIE_FREE(n);
+}
+
+static KTRIEPATTERN* KTrieNewPattern(unsigned char* P, int n)
+{
+ KTRIEPATTERN* p;
+
+ if (n < 1)
+ return nullptr;
+
+ p = (KTRIEPATTERN*)KTRIE_MALLOC(sizeof(KTRIEPATTERN) );
+
+ if (p == nullptr)
+ return nullptr;
+
+ /* Save as a nocase string */
+ p->P = (unsigned char*)KTRIE_MALLOC(n);
+ if ( !p->P )
+ {
+ KTRIE_FREE(p);
+ return nullptr;
+ }
+
+ 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 nullptr;
+ }
+
+ memcpy(p->Pcase, P, n);
+
+ p->n = n;
+ p->next = nullptr;
+
+ 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 = nullptr;
+
+ 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!=nullptr; 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!=nullptr; plist=plist->next )
+ {
+ int shift, cindex;
+
+ for ( k=0; k<kt->bcSize; 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(nullptr, &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 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;
+}
+
+/*
+* Build the Keyword TRIE
+*
+*/
+int KTrieCompile(KTRIE_STRUCT* ts,
+ int (* build_tree)(void* id, void** existing_tree),
+ int (* neg_list_func)(void* id, void** list))
+{
+ 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);
+ */
+ if (build_tree && neg_list_func)
+ {
+ KTrieBuildMatchStateTrees(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, KTRIEPATTERN* 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; i<q->inq; 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,
+ const unsigned char* T,
+ int n,
+ int (* match)(void* id, void* tree, int index, void* data, void* neg_list),
+ void* data)
+{
+ KTRIENODE* root;
+
+ 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,
+ const unsigned char* T,
+ const unsigned char*,
+ const 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, const 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, const unsigned char* Tx,
+ int n,
+ int (* match)(void* id, void* tree, int index, void* data, void* neg_list),
+ void* data)
+{
+ int nfound = 0;
+ const 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, const unsigned char* Tx,
+ int n,
+ int (* match)(void* id, void* tree, int index, void* data, void* neg_list),
+ void* data)
+{
+ int tshift;
+ const unsigned char* Tend;
+ const 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, const 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<argc; i++)
+ {
+ if ( strcmp(argv[i],"-c")==0 )
+ nocase=0; /* ignore case */
+ }
+
+ printf("New TRIE created\n");
+
+ for (i=2; i<argc; i++)
+ {
+ if ( argv[i][0]=='-' )
+ continue;
+
+ KTrieAddPattern(ts, (unsigned char*)argv[i], strlen(argv[i]), nocase, i);
+ }
+
+ printf("Patterns added \n");
+
+ KTrieCompile(ts);
+
+ printf("Patterns compiled \n");
+ printf("--> %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
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 Cisco and/or its affiliates. All rights reserved.
+// Copyright (C) 2003-2013 Sourcefire, Inc.
+// Copyright (C) 2001 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.
+//--------------------------------------------------------------------------
+
+// sfksearch.h author Sourcefire Inc.
+
+#ifndef KTRIE_H
+#define KTRIE_H
+
+// Trie based multi-pattern matcher
+
+#define ALPHABET_SIZE 256
+
+#ifdef WIN32
+#define inline __inline
+#endif
+
+#define KTRIEMETHOD_STD 0
+#define KTRIEMETHOD_QUEUE 1
+
+struct KTRIEPATTERN
+{
+ KTRIEPATTERN* next; /* global list of all patterns */
+ 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;
+};
+
+struct KTRIENODE
+{
+ int edge; /* character */
+
+ KTRIENODE* sibling;
+ KTRIENODE* child;
+
+ KTRIEPATTERN* pkeyword;
+};
+
+#define KTRIE_ROOT_NODES 256
+
+#define SFK_MAX_INQ 32
+struct SFK_PMQ
+{
+ unsigned inq;
+ unsigned inq_flush;
+ KTRIEPATTERN* q[SFK_MAX_INQ];
+};
+
+struct KTRIE_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* 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));
+int KTrieSearch(KTRIE_STRUCT* ts, const unsigned char* T, int n,
+ int (* match)(void* id, void* tree, int index, void* data, void* neg_list),
+ void* data);
+unsigned int KTrieMemUsed();
+void KTrieInitMemUsed();
+void KTrieDelete(KTRIE_STRUCT* k);
+int KTriePatternCount(KTRIE_STRUCT* k);
+
+void sfksearch_print_qinfo();
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// sfutil.cc author Sourcefire Inc.
+
+#include "sfutil.h"
+#include "common_util.h"
+
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "utils/util.h"
+
+void ConfigItemFree(ConfigItem* ci)
+{
+ if (ci)
+ {
+ if (ci->name)
+ snort_free(ci->name);
+ if (ci->value)
+ snort_free(ci->value);
+ snort_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)) != nullptr; )
+ {
+ 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, " ")) != nullptr; )
+ {
+ if (**ap != '\0')
+ {
+ ap++;
+ argcount++;
+ }
+ }
+
+ *ap = nullptr;
+
+ /* scan for comments */
+ while (i < argcount)
+ {
+ tok = toklist[i];
+
+ if (tok[0] == '#' && !drop_further)
+ {
+ argcount = i;
+ drop_further = 1;
+ }
+
+ if (drop_further)
+ {
+ toklist[i] = nullptr;
+ }
+
+ i++;
+ }
+
+ return argcount;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 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.
+//--------------------------------------------------------------------------
+
+// sfutil.h author Sourcefire Inc.
+
+#ifndef SFUTIL_H
+#define SFUTIL_H
+
+#include <stdint.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
+
#endif
#include "framework/inspector.h"
+extern const BaseApi* nin_appid_inspector;
extern const BaseApi* nin_binder;
extern const BaseApi* nin_normalize;
extern const BaseApi* nin_perf_monitor;
const BaseApi* network_inspectors[] =
{
+ nin_appid_inspector,
nin_binder,
nin_normalize,
nin_perf_monitor,
obj, T, n, match, context, 0 /* start-state */, current_state);
}
+ // FIXIT-L: Implement search_all method for AC_BNFA.
+
int print_info() override
{
bnfaPrintInfo(obj);
#include "framework/mpse.h"
#include "managers/mpse_manager.h"
-SearchTool::SearchTool()
+SearchTool::SearchTool() : SearchTool("ac_bnfa")
{
- mpse = MpseManager::get_search_engine("ac_bnfa");
+}
+
+SearchTool::SearchTool(const char* method)
+{
+ mpse = MpseManager::get_search_engine(method);
max_len = 0;
}
add((uint8_t*)pat, len, id, no_case);
}
+void SearchTool::add(const char* pat, unsigned len, void* id, bool no_case)
+{
+ add((uint8_t*)pat, len, id, no_case);
+}
+
void SearchTool::add(const uint8_t* pat, unsigned len, int id, bool no_case)
+{
+ add(pat, len, (void*)(long)id, no_case);
+}
+
+void SearchTool::add(const uint8_t* pat, unsigned len, void* id, bool no_case)
{
Mpse::PatternDescriptor desc(no_case);
if ( mpse )
- mpse->add_pattern(nullptr, pat, len, desc, (void*)(long)id);
+ mpse->add_pattern(nullptr, pat, len, desc, id);
if ( len > max_len )
max_len = len;
{
public:
SearchTool();
+ SearchTool(const char* method);
~SearchTool();
void add(const char* pattern, unsigned len, int s_id, bool no_case = true);
void add(const uint8_t* pattern, unsigned len, int s_id, bool no_case = true);
+ void add(const char* pattern, unsigned len, void* s_context, bool no_case = true);
+ void add(const uint8_t* pattern, unsigned len, void* s_context, bool no_case = true);
void prep();
AM_DEFAULT_SOURCE_EXT = .cc
-if HAVE_HYPERSCAN
check_PROGRAMS = \
-hyperscan_test
+search_tool_test
TESTS = $(check_PROGRAMS)
+search_tool_test_CPPFLAGS = $(AM_CPPFLAGS) @CPPUTEST_CPPFLAGS@
+search_tool_test_LDADD = \
+../libsearch_engines.a \
+../../catch/unit_test.o \
+@CPPUTEST_LDFLAGS@
+
+if HAVE_HYPERSCAN
+check_PROGRAMS += hyperscan_test
+
hyperscan_test_CPPFLAGS = $(AM_CPPFLAGS) @CPPUTEST_CPPFLAGS@
hyperscan_test_LDADD = \
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2016-2016 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.
+//--------------------------------------------------------------------------
+
+// search_tool_test.cc author Steve Chew <stechew@cisco.com>
+
+// Change private to public to give access to private members.
+#define private public
+#include "search_engines/search_tool.h"
+#undef private
+
+#include <string.h>
+
+#include "framework/base_api.h"
+#include "framework/mpse.h"
+#include "managers/mpse_manager.h"
+#include "main/snort_config.h"
+
+// must appear after snort_config.h to avoid broken c++ map include
+#include <CppUTest/CommandLineTestRunner.h>
+#include <CppUTest/TestHarness.h>
+
+
+//-------------------------------------------------------------------------
+// base stuff
+//-------------------------------------------------------------------------
+
+SnortConfig s_conf;
+THREAD_LOCAL SnortConfig* snort_conf = &s_conf;
+
+static SnortState s_state;
+
+SnortConfig::SnortConfig()
+{
+ state = &s_state;
+ memset(state, 0, sizeof(*state));
+ num_slots = 1;
+}
+
+SnortConfig::~SnortConfig() { }
+
+unsigned get_instance_id()
+{ return 0; }
+
+FileIdentifier::~FileIdentifier() { }
+
+FileVerdict FilePolicy::type_lookup(Flow*, FileContext*)
+{ return FILE_VERDICT_UNKNOWN; }
+
+FileVerdict FilePolicy::type_lookup(Flow*, FileInfo*)
+{ return FILE_VERDICT_UNKNOWN; }
+
+FileVerdict FilePolicy::signature_lookup(Flow*, FileContext*)
+{ return FILE_VERDICT_UNKNOWN; }
+
+FileVerdict FilePolicy::signature_lookup(Flow*, FileInfo*)
+{ return FILE_VERDICT_UNKNOWN; }
+
+void LogValue(const char*, const char*, FILE* = stdout)
+{
+}
+
+SO_PUBLIC void LogMessage(const char*, ...)
+{
+}
+
+void FatalError(const char*,...)
+{
+ exit(1);
+}
+
+void LogCount(char const*, uint64_t, FILE*)
+{ }
+
+void LogStat(const char*, double, FILE* = stdout)
+{}
+
+static void* s_tree = (void*)"tree";
+static void* s_list = (void*)"list";
+
+static MpseAgent s_agent =
+{
+ [](struct SnortConfig* sc, void*, void** ppt)
+ {
+ CHECK(sc == nullptr);
+ *ppt = s_tree;
+ return 0;
+ },
+ [](void*, void** ppl)
+ {
+ *ppl = s_list;
+ return 0;
+ },
+
+ [](void*) { },
+ [](void** ppt) { CHECK(*ppt == s_tree); },
+ [](void** ppl) { CHECK(*ppl == s_list); }
+};
+
+extern const BaseApi* se_ac_full;
+const MpseApi* mpse_api = (MpseApi*)se_ac_full;
+Mpse* acf = nullptr;
+
+Mpse* MpseManager::get_search_engine(const char *type)
+{
+ acf = nullptr;
+
+ if(strcmp(type, "ac_full") == 0)
+ {
+ CHECK(se_ac_full);
+ mpse_api->init();
+ acf = mpse_api->ctor(snort_conf, nullptr, false, &s_agent);
+ CHECK(acf);
+ }
+
+ return acf;
+}
+
+void MpseManager::delete_search_engine(Mpse *)
+{
+ mpse_api->dtor(acf);
+}
+
+Mpse::Mpse(const char*, bool) { }
+
+int Mpse::search(
+ const unsigned char* T, int n, MpseMatch match,
+ void* context, int* current_state)
+{
+ return _search(T, n, match, context, current_state);
+}
+
+int Mpse::search_all(
+ const unsigned char* T, int n, MpseMatch match,
+ void* context, int* current_state)
+{
+ return _search(T, n, match, context, current_state);
+}
+
+uint64_t Mpse::get_pattern_byte_count()
+{ return 0; }
+
+void Mpse::reset_pattern_byte_count()
+{ }
+
+int pattern_id = 0;
+int Test_SearchStrFound(void* /* id */, void* /* tree */, int /* index */, void* /* context */, void* /* neg_list */)
+{
+ //printf("found str with id=%ld, index=%d\n", (long)id, index);
+ return 0;
+}
+
+TEST_GROUP(search_tool_tests)
+{
+ void setup()
+ {
+ CHECK(se_ac_full);
+ }
+};
+
+TEST(search_tool_tests, ac_full)
+{
+ // get_search_engine returns nullptr any search engine other than ac_full.
+ SearchTool *stool = new SearchTool;
+ CHECK(!stool->mpse);
+ delete stool;
+
+ stool = new SearchTool("ac_full");
+ CHECK(stool->mpse);
+
+ pattern_id = 1;
+ stool->add("the", 3, pattern_id);
+ CHECK(stool->max_len == 3);
+
+ pattern_id = 77;
+ stool->add("uba", 3, pattern_id);
+ CHECK(stool->max_len == 3);
+
+ pattern_id = 2112;
+ stool->add("away", 4, pattern_id);
+ CHECK(stool->max_len == 4);
+
+ pattern_id = 1000;
+ stool->add("nothere", 7, pattern_id);
+ CHECK(stool->max_len == 7);
+
+ stool->prep();
+
+ const char *datastr = "the tuba ran away";
+ int result = stool->find(datastr, strlen(datastr), Test_SearchStrFound);
+ CHECK(result == 3);
+ delete stool;
+}
+
+//-------------------------------------------------------------------------
+// main
+//-------------------------------------------------------------------------
+
+int main(int argc, char** argv)
+{
+ return CommandLineTestRunner::RunAllTests(argc, argv);
+}
+
/* uint64_t ip64[2]; */
};
+ inline void clear()
+ {
+ family = bits = 0;
+ ip32[0] = ip32[1] = ip32[2] = ip32[3] = 0;
+ }
+
inline bool is_ip6() const
{ return family == AF_INET6; }
add_library(preprocessor_states
+ pps_appid.cc
pps_arpspoof.cc
pps_bo.cc
pps_dcerpc.cc
noinst_LIBRARIES = libpreprocessor_states.a
libpreprocessor_states_a_SOURCES = \
+pps_appid.cc \
pps_arpspoof.cc \
pps_bo.cc \
pps_dcerpc.cc \
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2015-2016 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.
+//--------------------------------------------------------------------------
+// pps_appid.cc author davis mcpherson <davmcphe@cisco.com>
+
+#include <sstream>
+#include <vector>
+
+#include "conversion_state.h"
+#include "helpers/s2l_util.h"
+
+namespace preprocessors
+{
+namespace
+{
+class AppId : public ConversionState
+{
+public:
+ AppId(Converter& c) : ConversionState(c) { }
+ virtual ~AppId() { }
+ virtual bool convert(std::istringstream& data_stream);
+};
+} // namespace
+
+bool AppId::convert(std::istringstream& data_stream)
+{
+ std::string keyword;
+ bool retval = true;
+
+ table_api.open_table("appid");
+
+ // parse the file configuration
+ while (util::get_string(data_stream, keyword, ","))
+ {
+ bool tmpval = true;
+ std::istringstream arg_stream(keyword);
+
+ // should be guaranteed to happen. Checking for error just cause
+ if (!(arg_stream >> keyword))
+ tmpval = false;
+ else if (!keyword.compare("conf"))
+ {
+ std::string file_name;
+ if( arg_stream >> file_name)
+ {
+ tmpval = table_api.add_option("conf", file_name);
+ }
+ else
+ {
+ data_api.failed_conversion(arg_stream, "appid: conf <missing_arg>");
+ tmpval = false;
+ }
+ }
+ else if (!keyword.compare("memcap"))
+ {
+ tmpval = parse_int_option("memcap", arg_stream, false);
+ }
+ else if (!keyword.compare("debug"))
+ {
+ std::string val;
+ if (!(arg_stream >> val))
+ data_api.failed_conversion(arg_stream, "appid: debug <missing_arg>");
+ else if (!val.compare("yes"))
+ table_api.add_option("debug", true);
+ else
+ table_api.add_option("debug", false);
+ }
+ else if (!keyword.compare("dump_ports"))
+ {
+ table_api.add_option("dump_ports", true);
+ }
+ else if (!keyword.compare("instance_id"))
+ {
+ tmpval = parse_int_option("instance_id", arg_stream, false);
+ }
+ else if (!keyword.compare("app_stats_filename"))
+ {
+ std::string file_name;
+ if (arg_stream >> file_name)
+ {
+ tmpval = table_api.add_option("app_stats_filename", file_name);
+ }
+ else
+ {
+ data_api.failed_conversion(arg_stream, "appid: app_stats_filename <missing_arg>");
+ tmpval = false;
+ }
+ }
+ else if (!keyword.compare("app_stats_period"))
+ {
+ tmpval = parse_int_option("app_stats_period", arg_stream, false);
+ }
+ else if (!keyword.compare("app_stats_rollover_size"))
+ {
+ tmpval = parse_int_option("app_stats_rollover_size", arg_stream, false);
+ }
+ else if (!keyword.compare("app_stats_rollover_time"))
+ {
+ tmpval = parse_int_option("app_stats_rollover_time", arg_stream, false);
+ }
+ else if (!keyword.compare("app_detector_dir"))
+ {
+ std::string file_name;
+ if (arg_stream >> file_name)
+ {
+ tmpval = table_api.add_option("app_detector_dir", file_name);
+ }
+ else
+ {
+ data_api.failed_conversion(arg_stream, "appid: app_detector_dir <missing_arg>");
+ tmpval = false;
+ }
+ }
+ else if (!keyword.compare("thirdparty_appid_dir"))
+ {
+ std::string file_name;
+ if (arg_stream >> file_name)
+ {
+ tmpval = table_api.add_option("thirdparty_appid_dir", file_name);
+ }
+ else
+ {
+ data_api.failed_conversion(arg_stream, "appid: thirdparty_appid_dir <missing_arg>");
+ tmpval = false;
+ }
+ }
+ else
+ {
+ tmpval = false;
+ }
+
+ if (!tmpval)
+ {
+ data_api.failed_conversion(arg_stream, keyword);
+ retval = false;
+ }
+ }
+
+ return retval;
+}
+
+/**************************
+ ******* A P I ***********
+ **************************/
+
+static ConversionState* ctor(Converter& c)
+{
+ return new AppId(c);
+}
+
+static const ConvertMap preprocessor_appid =
+{
+ "appid",
+ ctor,
+};
+
+const ConvertMap* appid_map = &preprocessor_appid;
+}
+
namespace preprocessors
{
+extern const ConvertMap* appid_map;
extern const ConvertMap* arpspoof_map;
extern const ConvertMap* arpspoof_host_map;
extern const ConvertMap* bo_map;
const std::vector<const ConvertMap*> preprocessor_api =
{
+ appid_map,
arpspoof_map,
arpspoof_host_map,
bo_map,