]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #2386 in SNORT/snort3 from ~SBAIGAL/snort3:ftps to master
authorSteve Chew (stechew) <stechew@cisco.com>
Wed, 12 Aug 2020 01:44:45 +0000 (01:44 +0000)
committerSteve Chew (stechew) <stechew@cisco.com>
Wed, 12 Aug 2020 01:44:45 +0000 (01:44 +0000)
Squashed commit of the following:

commit 24e1fa41a6b82bc793fe90015a160ac6842876a7
Author: Steven Baigal (sbaigal) <sbaigal@cisco.com>
Date:   Thu Jul 23 15:38:58 2020 -0400

    ftp: add opportunistic TLS support

src/service_inspectors/ftp_telnet/ftp_data.cc
src/service_inspectors/ftp_telnet/ftp_module.cc
src/service_inspectors/ftp_telnet/ftp_module.h
src/service_inspectors/ftp_telnet/ftpp_si.h
src/service_inspectors/ftp_telnet/ftpp_ui_config.h
src/service_inspectors/ftp_telnet/pp_ftp.cc
src/service_inspectors/smtp/smtp.cc
src/service_inspectors/smtp/smtp_config.h
src/service_inspectors/ssl/ssl_inspector.cc
src/service_inspectors/ssl/ssl_inspector.h

index 95627ec2126f75e209860c371de3710f07c2bf1e..6543ac91d676e424a54fda7f304ab272024acda5 100644 (file)
@@ -30,6 +30,7 @@
 #include "packet_tracer/packet_tracer.h"
 #include "parser/parse_rule.h"
 #include "profiler/profiler.h"
+#include "pub_sub/opportunistic_tls_event.h"
 #include "stream/stream.h"
 #include "utils/util.h"
 
@@ -222,7 +223,17 @@ FtpDataFlowData::~FtpDataFlowData()
 void FtpDataFlowData::handle_expected(Packet* p)
 {
     if (!p->flow->service)
+    {
         p->flow->set_service(p, fd_svc_name);
+
+        FtpDataFlowData* fd =
+            (FtpDataFlowData*)p->flow->get_flow_data(FtpDataFlowData::inspector_id);
+        if (fd and fd->in_tls)
+        {
+            OpportunisticTlsEvent evt(p, fd_svc_name);
+            DataBus::publish(OPPORTUNISTIC_TLS_EVENT, evt, p->flow);
+        }   
+    }
 }
 
 void FtpDataFlowData::handle_eof(Packet* p)
index 042f3b54bdf06ea6188b15ec9f99b2b6be9d9d1d..212a28fe1aaf6a9b25ed41a973ad5916145f0bc2 100644 (file)
@@ -344,6 +344,9 @@ static const PegInfo ftp_pegs[] =
     { CountType::SUM, "total_bytes", "total number of bytes processed" },
     { CountType::NOW, "concurrent_sessions", "total concurrent FTP sessions" },
     { CountType::MAX, "max_concurrent_sessions", "maximum concurrent FTP sessions" },
+    { CountType::SUM, "start_tls", "total STARTTLS events generated" },
+    { CountType::SUM, "ssl_search_abandoned", "total SSL search abandoned" },
+    { CountType::SUM, "ssl_srch_abandoned_early", "total SSL search abandoned too soon" },
 
     { CountType::END, nullptr, nullptr }
 };
@@ -569,6 +572,9 @@ static int ProcessFTPDataChanCmdsList(
     if ( fc->flags & CMD_ENCR )
         FTPCmd->encr_cmd = true;
 
+    if ( fc->flags & CMD_PROT )
+        FTPCmd->prot_cmd = true;
+
     if ( fc->flags & CMD_LOGIN )
         FTPCmd->login_cmd = true;
 
@@ -599,6 +605,7 @@ bool FtpServerModule::end(const char* fqn, int idx, SnortConfig*)
 
     if ( !idx && !strcmp(fqn, "ftp_server") )
     {
+        cmds.emplace_back(new FtpCmd("PROT", CMD_PROT, 0));
         for( auto cmd : cmds)
         {
             if ( FTPP_SUCCESS !=  ProcessFTPDataChanCmdsList(conf, cmd) )
index 06597c950afc0d27b5f7812eeb7356323b878724..215a7ba70b39ee1a1e2700e38bd749c2e45ecfb5 100644 (file)
@@ -95,6 +95,7 @@ private:
 #define CMD_DIR    0x0100
 #define CMD_VALID  0x0200
 #define CMD_REST   0x0400
+#define CMD_PROT   0x0800
 
 struct FtpCmd
 {
index a5d30f0e7c1cb82e831318af710e02b253217c4b..0feeebaa70ae63b3d577894b59e33070cc0bef7a 100644 (file)
 #define PROTO_IS_FTP_DATA(ssn)          FTPP_SI_IS_PROTO(ssn, FTPP_SI_PROTO_FTP_DATA)
 #define PROTO_IS_TELNET(ssn)            FTPP_SI_IS_PROTO(ssn, FTPP_SI_PROTO_TELNET)
 
-#define FTP_FLG_MALWARE  (1<<0)
+#define FTP_FLG_MALWARE                 0x01
+#define FTP_FLG_SEARCH_ABANDONED        0x02
+#define FTP_PROTP_CMD_ISSUED            0x04
+#define FTP_PROTP_CMD_ACCEPT            0x08
 
 typedef struct s_FTP_TELNET_SESSION
 {
@@ -237,6 +240,7 @@ public:
     static unsigned inspector_id;
     FTP_DATA_SESSION session;
     bool eof_handled = false;
+    bool in_tls = false;
 };
 
 #define FTPDATA_FLG_REASSEMBLY_SET  (1<<0)
@@ -286,6 +290,9 @@ struct FtpStats
     PegCount total_bytes;
     PegCount concurrent_sessions;
     PegCount max_concurrent_sessions;
+    PegCount starttls;
+    PegCount ssl_search_abandoned;
+    PegCount ssl_search_abandoned_too_soon;
 };
 
 struct TelnetStats
index 6474a62d1c1fba41760e24cb739e2a221b643746..a7a5abfd8b651f711dff53bab8eeed5f46f86e2b 100644 (file)
@@ -161,6 +161,7 @@ typedef struct s_FTP_CMD_CONF
     bool file_get_cmd;
     bool encr_cmd;
     bool login_cmd;
+    bool prot_cmd;
     int dir_response;
 
     FTP_PARAM_FMT* param_format;
index 0b8b79702e80226f69f2ffd02a25098468a3c8ee..a96300af8979619077f5aa4a4c60604047accee7 100644 (file)
@@ -45,6 +45,7 @@
 #include "hash/hash_key_operations.h"
 #include "file_api/file_service.h"
 #include "protocols/packet.h"
+#include "pub_sub/opportunistic_tls_event.h"
 #include "stream/stream.h"
 #include "utils/util.h"
 
@@ -1021,6 +1022,15 @@ static int do_stateful_checks(FTP_SESSION* session, Packet* p,
              * If we saw all the other dat for this channel
              * session->data_chan_state should be NO_STATE. */
         }
+        else if (session->flags & FTP_PROTP_CMD_ISSUED)
+        {
+            if (rsp_code == 200)
+            {
+                session->flags &= ~FTP_PROTP_CMD_ISSUED;
+                session->flags |= FTP_PROTP_CMD_ACCEPT;
+            }
+                
+        }
         else if (session->data_chan_state & DATA_CHAN_PASV_CMD_ISSUED)
         {
             if (ftp_cmd_pipe_index == session->data_chan_index)
@@ -1093,6 +1103,10 @@ static int do_stateful_checks(FTP_SESSION* session, Packet* p,
                                 if (session->flags & FTP_FLG_MALWARE)
                                     session->datassn = ftpdata;
 
+                                if (p->flow->flags.data_decrypted and
+                                    (session->flags & FTP_PROTP_CMD_ACCEPT))
+                                    fd->in_tls = true;
+
                                 /* Call into Streams to mark data channel as ftp-data */
                                 result = Stream::set_snort_protocol_id_expected(
                                     p, PktType::TCP, IpProtocol::TCP,
@@ -1171,6 +1185,10 @@ static int do_stateful_checks(FTP_SESSION* session, Packet* p,
                             if (session->flags & FTP_FLG_MALWARE)
                                 session->datassn = ftpdata;
 
+                            if (p->flow->flags.data_decrypted and
+                                (session->flags & FTP_PROTP_CMD_ACCEPT))
+                                fd->in_tls = true;
+
                             /* Call into Streams to mark data channel as ftp-data */
                             result = Stream::set_snort_protocol_id_expected(
                                 p, PktType::TCP, IpProtocol::TCP,
@@ -1248,6 +1266,16 @@ static int do_stateful_checks(FTP_SESSION* session, Packet* p,
         }
     } /* if (session->server_conf->data_chan) */
 
+    if ((session->encr_state == AUTH_TLS_CMD_ISSUED or session->encr_state == AUTH_SSL_CMD_ISSUED)
+        and rsp_code == 234)
+    {
+        OpportunisticTlsEvent event(p, p->flow->service);
+        DataBus::publish(OPPORTUNISTIC_TLS_EVENT, event, p->flow);
+        ++ftstats.starttls;
+        if (session->flags & FTP_FLG_SEARCH_ABANDONED)
+            ++ftstats.ssl_search_abandoned_too_soon;
+    }
+
     if (session->server_conf->detect_encrypted)
     {
         switch (session->encr_state)
@@ -1795,6 +1823,23 @@ int check_ftp(FTP_SESSION* ftpssn, Packet* p, int iMode)
                         ftpssn->encr_state = AUTH_UNKNOWN_CMD_ISSUED;
                     }
                 }
+                else if (CmdConf->prot_cmd)
+                {
+                    if (req->param_begin && (req->param_size > 0) &&
+                        ((req->param_begin[0] == 'P') || (req->param_begin[0] == 'p')))
+                    {
+                        ftpssn->flags &= ~FTP_PROTP_CMD_ACCEPT;
+                        ftpssn->flags |= FTP_PROTP_CMD_ISSUED;
+                    }
+                }
+                else if (CmdConf->login_cmd and !p->flow->flags.data_decrypted and
+                    !(ftpssn->flags & FTP_FLG_SEARCH_ABANDONED))
+                {
+                    ftpssn->flags |= FTP_FLG_SEARCH_ABANDONED;
+                    DataBus::publish(SSL_SEARCH_ABANDONED, p);
+                    ++ftstats.ssl_search_abandoned;
+                }
+
                 if (CmdConf->check_validity)
                 {
                     const char* next_param = nullptr;
index f8ed7046293a1a1bb49d310f23d65e51d0279967..8382f2a6fb4974ad5985f9b1dacaff0aaf4a32d3 100644 (file)
@@ -169,6 +169,9 @@ const PegInfo smtp_peg_names[] =
     { CountType::SUM, "sessions", "total smtp sessions" },
     { CountType::NOW, "concurrent_sessions", "total concurrent smtp sessions" },
     { CountType::MAX, "max_concurrent_sessions", "maximum concurrent smtp sessions" },
+    { CountType::SUM, "start_tls", "total STARTTLS events generated" },
+    { CountType::SUM, "ssl_search_abandoned", "total SSL search abandoned" },
+    { CountType::SUM, "ssl_srch_abandoned_early", "total SSL search abandoned too soon" },
     { CountType::SUM, "b64_attachments", "total base64 attachments decoded" },
     { CountType::SUM, "b64_decoded_bytes", "total base64 decoded bytes" },
     { CountType::SUM, "qp_attachments", "total quoted-printable attachments decoded" },
@@ -1096,6 +1099,9 @@ static void SMTP_ProcessServerPacket(
                 {
                     OpportunisticTlsEvent event(p, p->flow->service);
                     DataBus::publish(OPPORTUNISTIC_TLS_EVENT, event, p->flow);
+                    ++smtpstats.starttls;
+                    if (smtp_ssn->state_flags & SMTP_FLAG_ABANDON_EVT)
+                        ++smtpstats.ssl_search_abandoned_too_soon;
                 }
 
                 break;
@@ -1110,6 +1116,7 @@ static void SMTP_ProcessServerPacket(
                 {
                     smtp_ssn->state_flags |= SMTP_FLAG_ABANDON_EVT;
                     DataBus::publish(SSL_SEARCH_ABANDONED, p);
+                    ++smtpstats.ssl_search_abandoned;
                 }
                 break;
 
index bee0df255cd8c9c9edb282acae34eafb10c9feed..aada92fe732520c9ad3af413451572b166fd0bb3 100644 (file)
@@ -153,6 +153,9 @@ struct SmtpStats
     PegCount sessions;
     PegCount concurrent_sessions;
     PegCount max_concurrent_sessions;
+    PegCount starttls;
+    PegCount ssl_search_abandoned;
+    PegCount ssl_search_abandoned_too_soon;
     snort::MimeStats mime_stats;
 };
 
index 9b68088b486c63a028fd27796d09bb8868edd488..07acc8abcc878b8d804a8b973ed5278065893043 100644 (file)
@@ -77,6 +77,7 @@ const PegInfo ssl_peg_names[] =
 SslFlowData::SslFlowData() : FlowData(inspector_id)
 {
     memset(&session, 0, sizeof(session));
+    finalize_info = {};
     sslstats.concurrent_sessions++;
     if(sslstats.max_concurrent_sessions < sslstats.concurrent_sessions)
         sslstats.max_concurrent_sessions = sslstats.concurrent_sessions;
@@ -431,6 +432,10 @@ public:
 
     void handle(DataEvent&, Flow* flow) override
     {
+        SslFlowData* fd = new SslFlowData;
+        fd->finalize_info.orig_flag = flow->flags.trigger_finalize_event;
+        fd->finalize_info.switch_in = true;
+        flow->set_flow_data(fd);
         flow->flags.trigger_finalize_event = true;
     }
 };
@@ -444,10 +449,14 @@ public:
     {
         FinalizePacketEvent* fp_event = (FinalizePacketEvent*)&e;
         const Packet* pkt = fp_event->get_packet();
-
-        pkt->flow->flags.trigger_finalize_event = false;
-        pkt->flow->set_proxied();
-        pkt->flow->set_service(const_cast<Packet*>(pkt), s_name);
+        SslFlowData* fd = (SslFlowData*)pkt->flow->get_flow_data(SslFlowData::inspector_id);
+        if (fd and fd->finalize_info.switch_in)
+        {
+            pkt->flow->flags.trigger_finalize_event = fd->finalize_info.orig_flag;
+            fd->finalize_info.switch_in = false;
+            pkt->flow->set_proxied();
+            pkt->flow->set_service(const_cast<Packet*>(pkt), s_name);
+        }
     }
 };
 
index 756137411dd0ebe4158a88e24abf48d6a2e5eb2b..56df1bb5cee641dc0a87867cb1609769d7e4de56 100644 (file)
@@ -51,6 +51,10 @@ public:
 public:
     static unsigned inspector_id;
     SSLData session;
+    struct {
+        bool orig_flag : 1;
+        bool switch_in : 1;
+    } finalize_info;
 };
 //Function: API to get the ssl flow data from the packet flow.
 SSLData* get_ssl_session_data(snort::Flow* flow);