]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Pull request #4449: pub_sub: add request and response events
authorAnna Norokh -X (anorokh - SOFTSERVE INC at Cisco) <anorokh@cisco.com>
Fri, 27 Sep 2024 14:03:54 +0000 (14:03 +0000)
committerOleksii Shumeiko -X (oshumeik - SOFTSERVE INC at Cisco) <oshumeik@cisco.com>
Fri, 27 Sep 2024 14:03:54 +0000 (14:03 +0000)
Merge in SNORT/snort3 from ~ANOROKH/snort3:extractor_ftp_event to master

Squashed commit of the following:

commit 45a8734430fa07e7e0898180e82508531efe0cdd
Author: anorokh <anorokh@cisco.com>
Date:   Mon Sep 16 16:19:15 2024 +0300

    pub_sub: add request and response FTP events

src/pub_sub/CMakeLists.txt
src/pub_sub/ftp_events.h [new file with mode: 0644]
src/pub_sub/test/CMakeLists.txt
src/pub_sub/test/pub_sub_ftp_events_test.cc [new file with mode: 0644]
src/service_inspectors/ftp_telnet/ftp.cc

index 2306be5148a313ee707fb293b853863914ce3e7a..29710692b080ab5284fda840b4ae789ee092780e 100644 (file)
@@ -12,6 +12,7 @@ set (PUB_SUB_INCLUDES
     expect_events.h
     external_event_ids.h
     finalize_packet_event.h
+    ftp_events.h
     http_event_ids.h
     http_events.h
     http_request_body_event.h
diff --git a/src/pub_sub/ftp_events.h b/src/pub_sub/ftp_events.h
new file mode 100644 (file)
index 0000000..1d0fab6
--- /dev/null
@@ -0,0 +1,86 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2024 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.
+//--------------------------------------------------------------------------
+
+// ftp_events.h author Anna Norokh <anorokh@cisco.com>
+
+#ifndef FTP_EVENTS_H
+#define FTP_EVENTS_H
+
+#include "framework/data_bus.h"
+#include "service_inspectors/ftp_telnet/ftpp_si.h"
+
+namespace snort
+{
+
+struct FtpEventIds
+{
+    enum : unsigned
+    {
+        FTP_REQUEST,
+        FTP_RESPONSE,
+        MAX
+    };
+};
+
+const snort::PubKey ftp_pub_key { "ftp", FtpEventIds::MAX };
+
+class SO_PUBLIC FtpRequestEvent : public snort::DataEvent
+{
+public:
+    FtpRequestEvent(const FTP_SESSION& ssn) : session(ssn) { }
+
+    const FTP_CLIENT_REQ& get_request() const
+    { return session.client.request; }
+
+    uint64_t get_client_port() const
+    { return (uint64_t)session.clientPort; }
+
+    const snort::SfIp& get_client_ip() const
+    { return session.clientIP; }
+
+private:
+    const FTP_SESSION& session;
+};
+
+class SO_PUBLIC FtpResponseEvent : public snort::DataEvent
+{
+public:
+    FtpResponseEvent(const FTP_SESSION& ssn) : session(ssn) { }
+
+    const FTP_SERVER_RSP& get_response() const
+    { return session.server.response; }
+
+    uint64_t get_client_port() const
+    { return (uint64_t)session.clientPort; }
+
+    uint64_t get_server_port() const
+    { return (uint64_t)session.serverPort; }
+
+    const snort::SfIp& get_client_ip() const
+    { return session.clientIP; }
+
+    const snort::SfIp& get_server_ip() const
+    { return session.serverIP; }
+
+private:
+    const FTP_SESSION& session;
+};
+
+}
+
+#endif
index 568ba067a7c8cecb136e36ceed3a6886b32775c1..f2a21d8aa29141abce751c4f4dd987bd14b9889c 100644 (file)
@@ -1,3 +1,9 @@
+if ( ENABLE_UNIT_TESTS )
+    add_library(extr_cpputest_deps OBJECT EXCLUDE_FROM_ALL
+        ../../sfip/sf_ip.cc
+    )
+endif ( ENABLE_UNIT_TESTS )
+
 add_cpputest( pub_sub_http_event_test
     SOURCES
         ../http_events.cc
@@ -20,3 +26,8 @@ add_cpputest( pub_sub_http_transaction_end_event_test
         ../../service_inspectors/http_inspect/http_test_input.cc
     LIBS ${ZLIB_LIBRARIES}
 )
+add_cpputest( pub_sub_ftp_events_test
+    SOURCES
+        ../ftp_events.h
+        $<TARGET_OBJECTS:extr_cpputest_deps>
+)
diff --git a/src/pub_sub/test/pub_sub_ftp_events_test.cc b/src/pub_sub/test/pub_sub_ftp_events_test.cc
new file mode 100644 (file)
index 0000000..81187ec
--- /dev/null
@@ -0,0 +1,127 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2024 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.
+//--------------------------------------------------------------------------
+
+// ftp_events.h author Anna Norokh <anorokh@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string>
+
+#include "pub_sub/ftp_events.h"
+#include "service_inspectors/ftp_telnet/ftpp_si.h"
+#include "utils/util_net.h"
+
+#include <CppUTest/CommandLineTestRunner.h>
+#include <CppUTest/TestHarness.h>
+#include <CppUTestExt/MockSupport.h>
+
+using namespace snort;
+
+namespace snort
+{
+void* snort_alloc(size_t sz)
+{ return new uint8_t[sz]; }
+
+char* snort_strdup(const char* str)
+{
+    assert(str);
+    size_t n = strlen(str) + 1;
+    char* p = (char*)snort_alloc(n);
+    memcpy(p, str, n);
+    return p;
+}
+}
+
+static FTP_SESSION session;
+
+TEST_GROUP(pub_sub_ftp_events_test)
+{
+    const char* raw_request = "RETR file.txt\r\n";
+    const char* raw_response = "226 Transfer complete.\r\n";
+
+    void setup() override
+    {
+        session.client.request.cmd_begin = raw_request;
+        session.client.request.cmd_end = raw_request + 4;
+        session.client.request.cmd_size = 4;
+        session.client.request.param_begin = raw_request + 5;
+        session.client.request.param_end = raw_request + 13;
+        session.client.request.param_size = 8;
+
+        session.server.response.rsp_begin = const_cast<char*>(raw_response);
+        session.server.response.rsp_end = const_cast<char*>(raw_response) + 3;
+        session.server.response.rsp_size = 3;
+        session.server.response.msg_begin = const_cast<char*>(raw_response) + 4;
+        session.server.response.msg_end = const_cast<char*>(raw_response) + 22;
+        session.server.response.msg_size = 18;
+
+        session.clientIP.set("10.10.10.1");
+        session.serverIP.set("10.10.10.2");
+        session.clientPort = 40000;
+        session.serverPort = 20;
+    }
+};
+
+TEST(pub_sub_ftp_events_test, ftp_request_event)
+{
+    FtpRequestEvent event(session);
+
+    auto cmd = std::string(event.get_request().cmd_begin, event.get_request().cmd_size);
+    auto param = std::string(event.get_request().param_begin, event.get_request().param_size);
+    CHECK(cmd == "RETR");
+    CHECK(event.get_request().cmd_size == 4);
+    CHECK(param == "file.txt");
+    CHECK(event.get_request().param_size == 8);
+
+    InetBuf src;
+    sfip_ntop(&event.get_client_ip(), src, sizeof(src));
+    std::string client = src;
+    CHECK(client == "10.10.10.1");
+    CHECK(event.get_client_port() == 40000);
+}
+
+TEST(pub_sub_ftp_events_test, ftp_response_event)
+{
+    const FtpResponseEvent event(session);
+
+    auto response = event.get_response();
+    auto rsp = std::string(response.rsp_begin, response.rsp_size);
+    auto msg = std::string(response.msg_begin, response.msg_size);
+    CHECK(rsp == "226");
+    CHECK(response.rsp_size == 3);
+    CHECK(msg == "Transfer complete.");
+    CHECK(response.msg_size == 18);
+
+    InetBuf dst, src;
+    sfip_ntop(&event.get_client_ip(), src, sizeof(src));
+    sfip_ntop(&event.get_server_ip(), dst, sizeof(dst));
+    std::string client = src;
+    std::string server = dst;
+    CHECK(client == "10.10.10.1");
+    CHECK(event.get_client_port() == 40000);
+    CHECK(server == "10.10.10.2");
+    CHECK(event.get_server_port() == 20);
+
+}
+
+int main(int argc, char** argv)
+{
+    return CommandLineTestRunner::RunAllTests(argc, argv);
+}
index 37ae42e352ee2694afc93129f9d50dbeaf8ba28c..51f67c42243ea972ab78a4cb997892d591168ecc 100644 (file)
 #include "config.h"
 #endif
 
+#include <atomic>
+
+#include "framework/data_bus.h"
 #include "framework/pig_pen.h"
 #include "main/snort_config.h"
 #include "profiler/profiler.h"
 #include "protocols/packet.h"
+#include "pub_sub/ftp_events.h"
 #include "stream/stream.h"
 #include "target_based/snort_protocols.h"
 #include "utils/util.h"
@@ -51,6 +55,8 @@ SnortProtocolId ftp_data_snort_protocol_id = UNKNOWN_PROTOCOL_ID;
 THREAD_LOCAL ProfileStats ftpPerfStats;
 THREAD_LOCAL FtpStats ftstats;
 
+static std::atomic<unsigned> pub_id = 0;
+
 //-------------------------------------------------------------------------
 // implementation stuff
 //-------------------------------------------------------------------------
@@ -60,16 +66,28 @@ static inline int InspectClientPacket(Packet* p)
     return p->has_paf_payload();
 }
 
+static void publish_ftp_request(const FTP_SESSION& session, Flow* flow)
+{
+    FtpRequestEvent event(session);
+    DataBus::publish(pub_id, FtpEventIds::FTP_REQUEST, event, flow);
+}
+
+static void publish_ftp_response(const FTP_SESSION& session, Flow* flow)
+{
+    FtpResponseEvent event(session);
+    DataBus::publish(pub_id, FtpEventIds::FTP_RESPONSE, event, flow);
+}
+
 static int SnortFTP(
     FTP_SESSION* FTPsession, Packet* p, int iInspectMode)
 {
     // cppcheck-suppress unreadVariable
     Profile profile(ftpPerfStats);
 
-    if ( !FTPsession || !FTPsession->server_conf || !FTPsession->client_conf )
+    if (!FTPsession || !FTPsession->server_conf || !FTPsession->client_conf)
         return FTPP_INVALID_SESSION;
 
-    if ( !FTPsession->server_conf->check_encrypted_data )
+    if (!FTPsession->server_conf->check_encrypted_data)
     {
         if ( FTPsession->encr_state == AUTH_TLS_ENCRYPTED ||
              FTPsession->encr_state == AUTH_SSL_ENCRYPTED ||
@@ -84,15 +102,15 @@ static int SnortFTP(
         //if ( !ScPafEnabled() )
         Stream::flush_client(p);
     }
-    else if ( !InspectClientPacket(p) )
+    else if (!InspectClientPacket(p))
         return FTPP_SUCCESS;
 
     int ret = initialize_ftp(FTPsession, p, iInspectMode);
-    if ( ret )
+    if (ret)
         return ret;
 
     ret = check_ftp(FTPsession, p, iInspectMode);
-    if ( ret == FTPP_SUCCESS )
+    if (ret == FTPP_SUCCESS)
     {
         // FIXIT-L ideally do_detection will look at the cmd & param buffers
         // or the rsp & msg buffers.  We should call it from inside check_ftp
@@ -101,6 +119,13 @@ static int SnortFTP(
         do_detection(p);
     }
 
+    assert(FTPsession);
+
+    if (iInspectMode == FTPP_SI_CLIENT_MODE)
+        publish_ftp_request(*FTPsession, p->flow);
+    else if (iInspectMode == FTPP_SI_SERVER_MODE)
+        publish_ftp_response(*FTPsession, p->flow);
+
     return ret;
 }
 
@@ -238,6 +263,7 @@ FtpServer::~FtpServer ()
 
 bool FtpServer::configure(SnortConfig* sc)
 {
+    pub_id = DataBus::get_id(ftp_pub_key);
     ftp_data_snort_protocol_id = sc->proto_ref->add("ftp-data");
     return !FTPCheckConfigs(sc, ftp_server);
 }