From 27ad0eac4fe7538a1cd1ec5c9683a10a0197d5df Mon Sep 17 00:00:00 2001 From: "Tom Peters (thopeter)" Date: Thu, 25 Aug 2016 12:52:05 -0400 Subject: [PATCH] Merge pull request #603 in SNORT/snort3 from appid_rsync1 to master Squashed commit of the following: commit d9f8bd1751d2deb3e9682a648b0b22c7bfb6c583 Merge: 011b8de 1c9b2e3 Author: Steve Chew Date: Wed Aug 24 12:34:30 2016 -0400 Merge branch 'appid_rsync1' of ssh://bitbucket-eng-rtp1.cisco.com:7999/snort/snort3 into appid_rsync1 commit 011b8de0c61fea1413025d9b8a74c0c9ad823fb3 Author: Steve Chew Date: Wed Aug 24 12:22:31 2016 -0400 Fixed Cmake test build. commit e25092d29345716ea5ce491232ee79251ea1727e Author: Steve Chew Date: Tue Aug 23 09:50:02 2016 -0400 include service_rsync.cc file in tests so we can access private data. commit 2703075fd0440ceb71c01be91d2ea3a28ba0f0fe Author: Steve Chew Date: Fri Aug 19 16:36:50 2016 -0400 Added rsync flow counter and rsync_validate unit tests. commit 1c9b2e3b28f68a488e264b26ea10f5fe23e5073b Author: Steve Chew Date: Tue Aug 23 09:50:02 2016 -0400 include service_rsync.cc file in tests so we can access private data. commit 0257acc0f2432d05684f28c4f3efc9b721a84eb2 Author: Steve Chew Date: Fri Aug 19 16:36:50 2016 -0400 Added rsync flow counter and rsync_validate unit tests. --- configure.ac | 1 + src/network_inspectors/appid/CMakeLists.txt | 3 +- src/network_inspectors/appid/Makefile.am | 8 +- src/network_inspectors/appid/appid_module.cc | 3 +- src/network_inspectors/appid/appid_module.h | 1 + .../appid/appid_stats_counter.cc | 26 ++ .../appid/service_plugins/service_rsync.cc | 7 + .../appid/service_plugins/service_rsync.h | 3 +- .../appid/service_plugins/test/CMakeLists.txt | 8 + .../appid/service_plugins/test/Makefile.am | 13 + .../test/service_rsync_test.cc | 346 ++++++++++++++++++ 11 files changed, 411 insertions(+), 8 deletions(-) create mode 100644 src/network_inspectors/appid/appid_stats_counter.cc create mode 100644 src/network_inspectors/appid/service_plugins/test/CMakeLists.txt create mode 100644 src/network_inspectors/appid/service_plugins/test/Makefile.am create mode 100644 src/network_inspectors/appid/service_plugins/test/service_rsync_test.cc diff --git a/configure.ac b/configure.ac index 236aace52..2673c9dd5 100644 --- a/configure.ac +++ b/configure.ac @@ -1158,6 +1158,7 @@ src/stream/user/Makefile \ src/stream/file/Makefile \ src/network_inspectors/Makefile \ src/network_inspectors/appid/Makefile \ +src/network_inspectors/appid/service_plugins/test/Makefile \ src/network_inspectors/arp_spoof/Makefile \ src/network_inspectors/binder/Makefile \ src/network_inspectors/normalize/Makefile \ diff --git a/src/network_inspectors/appid/CMakeLists.txt b/src/network_inspectors/appid/CMakeLists.txt index a07d58b12..8c0110e5d 100644 --- a/src/network_inspectors/appid/CMakeLists.txt +++ b/src/network_inspectors/appid/CMakeLists.txt @@ -147,6 +147,7 @@ set ( APPID_SOURCES appid_module.cc appid_module.h appid_stats.cc + appid_stats_counter.cc appid_stats.h app_info_table.cc app_info_table.h @@ -197,7 +198,7 @@ endif (STATIC_INSPECTORS) target_include_directories ( appid PRIVATE ${APPID_INCLUDE_DIR} ) -# FIXIT-H: Add unit tests +add_subdirectory(service_plugins/test) #install (FILES ${APPID_INCLUDES} # DESTINATION "${INCLUDE_INSTALL_PATH}/appid" diff --git a/src/network_inspectors/appid/Makefile.am b/src/network_inspectors/appid/Makefile.am index fe0c777d7..2aa2212b1 100644 --- a/src/network_inspectors/appid/Makefile.am +++ b/src/network_inspectors/appid/Makefile.am @@ -143,6 +143,7 @@ appid_inspector.h \ appid_module.cc \ appid_module.h \ appid_stats.cc \ +appid_stats_counter.cc \ appid_stats.h \ app_info_table.cc \ app_info_table.h \ @@ -190,8 +191,7 @@ $(dp_file_list) \ $(util_file_list) endif -#if BUILD_CPPUTESTS -#SUBDIRS = test -#endif - +if ENABLE_UNIT_TESTS +SUBDIRS=service_plugins/test +endif diff --git a/src/network_inspectors/appid/appid_module.cc b/src/network_inspectors/appid/appid_module.cc index c77c20f8e..ef7fce3ff 100644 --- a/src/network_inspectors/appid/appid_module.cc +++ b/src/network_inspectors/appid/appid_module.cc @@ -59,6 +59,7 @@ const PegInfo appid_pegs[] = { "mysql_flows", "count of mysql service flows discovered by appid" }, { "netbios_flows", "count of netbios service flows discovered by appid" }, { "pop_flows", "count of pop service flows discovered by appid" }, + { "rsync_flows", "count of rsync service flows discovered by appid" }, { "smtp_flows", "count of smtp flows discovered by appid" }, { "smtps_flows", "count of smtps flows discovered by appid" }, { "ssh_clients", "count of ssh clients discovered by appid" }, @@ -69,8 +70,6 @@ const PegInfo appid_pegs[] = { nullptr, nullptr } }; -THREAD_LOCAL AppIdStats appid_stats; - static const Parameter s_params[] = { { "conf", Parameter::PT_STRING, nullptr, nullptr, diff --git a/src/network_inspectors/appid/appid_module.h b/src/network_inspectors/appid/appid_module.h index 093288c96..2375ad13d 100644 --- a/src/network_inspectors/appid/appid_module.h +++ b/src/network_inspectors/appid/appid_module.h @@ -60,6 +60,7 @@ struct AppIdStats PegCount mysql_flows; PegCount netbios_flows; PegCount pop_flows; + PegCount rsync_flows; PegCount smtp_flows; PegCount smtps_flows; PegCount ssh_clients; diff --git a/src/network_inspectors/appid/appid_stats_counter.cc b/src/network_inspectors/appid/appid_stats_counter.cc new file mode 100644 index 000000000..098ab8424 --- /dev/null +++ b/src/network_inspectors/appid/appid_stats_counter.cc @@ -0,0 +1,26 @@ +//-------------------------------------------------------------------------- +// 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 Steve Chew +// Created on: Auguest 18, 2016 + +#include "main/thread.h" +#include "appid_module.h" + +THREAD_LOCAL AppIdStats appid_stats; + diff --git a/src/network_inspectors/appid/service_plugins/service_rsync.cc b/src/network_inspectors/appid/service_plugins/service_rsync.cc index fc59e5cb7..a6574d38c 100644 --- a/src/network_inspectors/appid/service_plugins/service_rsync.cc +++ b/src/network_inspectors/appid/service_plugins/service_rsync.cc @@ -24,6 +24,7 @@ #include "application_ids.h" #include "service_api.h" #include "app_info_table.h" +#include "appid_module.h" #include "main/snort_debug.h" #include "utils/util.h" @@ -102,6 +103,11 @@ static int rsync_validate(ServiceValidationArgs* args) { ServiceRSYNCData* rd; int i; + + // FIXIT-L: Should this be an assert instead? + if (!args) + return SERVICE_NOMATCH; + AppIdData* flowp = args->flowp; const uint8_t* data = args->data; uint16_t size = args->size; @@ -155,6 +161,7 @@ inprocess: success: rsync_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element, APP_ID_RSYNC, nullptr, nullptr, nullptr); + appid_stats.rsync_flows++; return SERVICE_SUCCESS; fail: diff --git a/src/network_inspectors/appid/service_plugins/service_rsync.h b/src/network_inspectors/appid/service_plugins/service_rsync.h index 8782a1f77..ee4103027 100644 --- a/src/network_inspectors/appid/service_plugins/service_rsync.h +++ b/src/network_inspectors/appid/service_plugins/service_rsync.h @@ -22,7 +22,8 @@ #ifndef SERVICE_RSYNC_H #define SERVICE_RSYNC_H -struct RNAServiceValidationModule; +#include "service_api.h" + // FIXIT-L: Make the globals const or, if necessary, thread-local. extern RNAServiceValidationModule rsync_service_mod; diff --git a/src/network_inspectors/appid/service_plugins/test/CMakeLists.txt b/src/network_inspectors/appid/service_plugins/test/CMakeLists.txt new file mode 100644 index 000000000..d17e3f81b --- /dev/null +++ b/src/network_inspectors/appid/service_plugins/test/CMakeLists.txt @@ -0,0 +1,8 @@ + +add_library(depends_on_lib ../../appid_stats_counter.cc) + +add_cpputest(service_rsync_test depends_on_lib) + +include_directories ( appid PRIVATE ${APPID_INCLUDE_DIR} ) + + diff --git a/src/network_inspectors/appid/service_plugins/test/Makefile.am b/src/network_inspectors/appid/service_plugins/test/Makefile.am new file mode 100644 index 000000000..9f3d140e9 --- /dev/null +++ b/src/network_inspectors/appid/service_plugins/test/Makefile.am @@ -0,0 +1,13 @@ + +AM_DEFAULT_SOURCE_EXT = .cc + +check_PROGRAMS = \ +service_rsync_test + +TESTS = $(check_PROGRAMS) + +service_rsync_test_CPPFLAGS = -I$(top_srcdir)/src/network_inspectors/appid @AM_CPPFLAGS@ @CPPUTEST_CPPFLAGS@ +service_rsync_test_LDADD = \ +../../appid_stats_counter.o \ +@CPPUTEST_LDFLAGS@ + diff --git a/src/network_inspectors/appid/service_plugins/test/service_rsync_test.cc b/src/network_inspectors/appid/service_plugins/test/service_rsync_test.cc new file mode 100644 index 000000000..bbc0aac3f --- /dev/null +++ b/src/network_inspectors/appid/service_plugins/test/service_rsync_test.cc @@ -0,0 +1,346 @@ +//-------------------------------------------------------------------------- +// 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. +//-------------------------------------------------------------------------- + +// service_rsync_test.cc author Steve Chew +// unit test for service_rsync + +#include +#include +#include + +#include "network_inspectors/appid/service_plugins/service_rsync.cc" + +void Debug::print(const char*, int, uint64_t, const char*, ...) { } + +extern int rsync_validate(ServiceValidationArgs*); + +int fake_service_inprocess(AppIdData*, const Packet*, int, const RNAServiceElement*) +{ + mock().actualCall("service_inprocess"); + return -1; +} + +ServiceRSYNCData* fake_rsync_data = NULL; + +void* fake_service_flowdata_get(AppIdData*, unsigned) +{ + mock().actualCall("data_get"); + return fake_rsync_data; +} + +int fake_data_add(AppIdData*, void* data, unsigned, AppIdFreeFCN) +{ + mock().actualCall("data_add"); + fake_rsync_data = (ServiceRSYNCData*)data; + return -1; +} + +int fake_fail_service(AppIdData*, const Packet*, int, const RNAServiceElement*, unsigned, const AppIdConfig*) +{ + mock().actualCall("fail_service"); + return -1; +} + +int fake_add_service(AppIdData*, const Packet*, int, + const RNAServiceElement*, AppId, const char*, const char *, + const RNAServiceSubtype*) +{ + mock().actualCall("add_service"); + return -1; +} + +const ServiceApi fake_serviceapi = +{ + &fake_service_flowdata_get, + &fake_data_add, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + &fake_add_service, + &fake_fail_service, + &fake_service_inprocess, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +TEST_GROUP(service_rsync) +{ + void setup() + { + fake_rsync_data = NULL; + } + + void teardown() + { + snort_free(fake_rsync_data); + mock().clear(); + } +}; + +TEST(service_rsync, rsync_validate_null_args) +{ + LONGS_EQUAL(SERVICE_NOMATCH, rsync_validate(nullptr)); +} + +TEST(service_rsync, rsync_validate_zero_size) +{ + ServiceValidationArgs args; + args.size = 0; + rsync_service_mod.api = &fake_serviceapi; + + mock().expectOneCall("service_inprocess"); + LONGS_EQUAL(SERVICE_INPROCESS, rsync_validate(&args)); + mock().checkExpectations(); +} + +TEST(service_rsync, rsync_validate_skip_data_from_client) +{ + ServiceValidationArgs args; + args.size = 1; + args.dir = APP_ID_FROM_INITIATOR; + rsync_service_mod.api = &fake_serviceapi; + + mock().expectOneCall("service_inprocess"); + LONGS_EQUAL(SERVICE_INPROCESS, rsync_validate(&args)); + mock().checkExpectations(); +} + +TEST(service_rsync, rsync_validate_no_rsync_data_size_too_small) +{ + ServiceValidationArgs args; + args.size = 1; + args.dir = APP_ID_FROM_RESPONDER; + rsync_service_mod.api = &fake_serviceapi; + + mock().strictOrder(); + mock().expectOneCall("data_get"); + mock().expectOneCall("data_add"); + mock().expectOneCall("fail_service"); + + LONGS_EQUAL(SERVICE_NOMATCH, rsync_validate(&args)); + + mock().checkExpectations(); +} + +#define RSYNC_BANNER_VALID RSYNC_BANNER "26\n" +#define RSYNC_BANNER_NO_LINEFEED RSYNC_BANNER "26" +#define RSYNC_BANNER_BAD_VERSION RSYNC_BANNER "26a\n" +#define RSYNC_BANNER_INVALID "INVALID: 26\n" +#define RSYNC_MOTD "motd\n" +#define RSYNC_MOTD_NO_LINEFEED "motd" +#define RSYNC_MOTD_INVALID_STR "mo\btd\n" + +TEST(service_rsync, rsync_validate_banner_missing_linefeed) +{ + ServiceValidationArgs args; + args.dir = APP_ID_FROM_RESPONDER; + args.data = (const uint8_t*)RSYNC_BANNER_NO_LINEFEED; + args.size = strlen((const char *)args.data); + rsync_service_mod.api = &fake_serviceapi; + + mock().strictOrder(); + mock().expectOneCall("data_get"); + mock().expectOneCall("data_add"); + mock().expectOneCall("fail_service"); + + LONGS_EQUAL(SERVICE_NOMATCH, rsync_validate(&args)); + + mock().checkExpectations(); +} + +TEST(service_rsync, rsync_validate_banner_bad_version) +{ + ServiceValidationArgs args; + args.dir = APP_ID_FROM_RESPONDER; + args.data = (const uint8_t*)RSYNC_BANNER_BAD_VERSION; + args.size = strlen((const char *)args.data); + rsync_service_mod.api = &fake_serviceapi; + + mock().strictOrder(); + mock().expectOneCall("data_get"); + mock().expectOneCall("data_add"); + mock().expectOneCall("fail_service"); + + LONGS_EQUAL(SERVICE_NOMATCH, rsync_validate(&args)); + + mock().checkExpectations(); +} + +TEST(service_rsync, rsync_validate_banner_invalid_text) +{ + ServiceValidationArgs args; + args.dir = APP_ID_FROM_RESPONDER; + args.data = (const uint8_t*)RSYNC_BANNER_INVALID; + args.size = strlen((const char *)args.data); + rsync_service_mod.api = &fake_serviceapi; + + mock().strictOrder(); + mock().expectOneCall("data_get"); + mock().expectOneCall("data_add"); + mock().expectOneCall("fail_service"); + + LONGS_EQUAL(SERVICE_NOMATCH, rsync_validate(&args)); + + mock().checkExpectations(); +} + +TEST(service_rsync, rsync_validate_banner_valid) +{ + ServiceValidationArgs args; + args.dir = APP_ID_FROM_RESPONDER; + args.data = (const uint8_t*)RSYNC_BANNER_VALID; + args.size = strlen((const char *)args.data); + rsync_service_mod.api = &fake_serviceapi; + + mock().strictOrder(); + mock().expectOneCall("data_get"); + mock().expectOneCall("data_add"); + mock().expectOneCall("service_inprocess"); + + LONGS_EQUAL(SERVICE_INPROCESS, rsync_validate(&args)); + LONGS_EQUAL(RSYNC_STATE_MOTD, fake_rsync_data->state); + + mock().checkExpectations(); +} + +TEST(service_rsync, rsync_validate_motd_no_linefeed) +{ + ServiceValidationArgs args; + args.dir = APP_ID_FROM_RESPONDER; + args.data = (const uint8_t*)RSYNC_MOTD_NO_LINEFEED; + args.size = strlen((const char *)args.data); + rsync_service_mod.api = &fake_serviceapi; + + fake_rsync_data = (ServiceRSYNCData*)snort_calloc(sizeof(ServiceRSYNCData)); + fake_rsync_data->state = RSYNC_STATE_MOTD; + + mock().strictOrder(); + mock().expectOneCall("data_get"); + mock().expectOneCall("fail_service"); + + LONGS_EQUAL(SERVICE_NOMATCH, rsync_validate(&args)); + LONGS_EQUAL(RSYNC_STATE_MOTD, fake_rsync_data->state); + + mock().checkExpectations(); +} + +TEST(service_rsync, rsync_validate_motd_invalid_str) +{ + ServiceValidationArgs args; + args.dir = APP_ID_FROM_RESPONDER; + args.data = (const uint8_t*)RSYNC_MOTD_INVALID_STR; + args.size = strlen((const char *)args.data); + rsync_service_mod.api = &fake_serviceapi; + + fake_rsync_data = (ServiceRSYNCData*)snort_calloc(sizeof(ServiceRSYNCData)); + fake_rsync_data->state = RSYNC_STATE_MOTD; + + mock().strictOrder(); + mock().expectOneCall("data_get"); + mock().expectOneCall("fail_service"); + + LONGS_EQUAL(SERVICE_NOMATCH, rsync_validate(&args)); + LONGS_EQUAL(RSYNC_STATE_MOTD, fake_rsync_data->state); + + mock().checkExpectations(); +} + +TEST(service_rsync, rsync_validate_motd_valid) +{ + ServiceValidationArgs args; + args.dir = APP_ID_FROM_RESPONDER; + args.data = (const uint8_t*)RSYNC_MOTD; + args.size = strlen((const char *)args.data); + rsync_service_mod.api = &fake_serviceapi; + + fake_rsync_data = (ServiceRSYNCData*)snort_calloc(sizeof(ServiceRSYNCData)); + fake_rsync_data->state = RSYNC_STATE_MOTD; + + mock().strictOrder(); + mock().expectOneCall("data_get"); + mock().expectOneCall("add_service"); + + LONGS_EQUAL(SERVICE_SUCCESS, rsync_validate(&args)); + LONGS_EQUAL(RSYNC_STATE_DONE, fake_rsync_data->state); + + mock().checkExpectations(); +} + +// It's an error to get another call to rsync_validate if we've +// already reached the DONE state. +TEST(service_rsync, rsync_validate_should_not_called_after_done) +{ + ServiceValidationArgs args; + args.dir = APP_ID_FROM_RESPONDER; + args.data = (const uint8_t*)RSYNC_MOTD; + args.size = strlen((const char *)args.data); + rsync_service_mod.api = &fake_serviceapi; + + fake_rsync_data = (ServiceRSYNCData*)snort_calloc(sizeof(ServiceRSYNCData)); + fake_rsync_data->state = RSYNC_STATE_MOTD; + + mock().strictOrder(); + mock().expectOneCall("data_get"); + mock().expectOneCall("add_service"); + mock().expectOneCall("data_get"); + mock().expectOneCall("fail_service"); + + LONGS_EQUAL(SERVICE_SUCCESS, rsync_validate(&args)); + LONGS_EQUAL(RSYNC_STATE_DONE, fake_rsync_data->state); + + LONGS_EQUAL(SERVICE_NOMATCH, rsync_validate(&args)); + mock().checkExpectations(); +} + +TEST(service_rsync, rsync_validate_count_rsync_flow_on_success) +{ + ServiceValidationArgs args; + args.dir = APP_ID_FROM_RESPONDER; + args.data = (const uint8_t*)RSYNC_MOTD; + args.size = strlen((const char *)args.data); + rsync_service_mod.api = &fake_serviceapi; + + fake_rsync_data = (ServiceRSYNCData*)snort_calloc(sizeof(ServiceRSYNCData)); + fake_rsync_data->state = RSYNC_STATE_MOTD; + + mock().strictOrder(); + mock().expectOneCall("data_get"); + mock().expectOneCall("add_service"); + + LONGS_EQUAL(SERVICE_SUCCESS, rsync_validate(&args)); + LONGS_EQUAL(RSYNC_STATE_DONE, fake_rsync_data->state); + LONGS_EQUAL(1, appid_stats.rsync_flows); + + mock().checkExpectations(); +} + +int main(int argc, char** argv) +{ + int return_value = CommandLineTestRunner::RunAllTests(argc, argv); + return return_value; +} + -- 2.47.2