]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Pull request #3940: wizard: refactoring - split curses to multiple files by protocol
authorMaya Dagon (mdagon) <mdagon@cisco.com>
Fri, 4 Aug 2023 14:04:59 +0000 (14:04 +0000)
committerOleksii Shumeiko -X (oshumeik - SOFTSERVE INC at Cisco) <oshumeik@cisco.com>
Fri, 4 Aug 2023 14:04:59 +0000 (14:04 +0000)
Merge in SNORT/snort3 from ~MDAGON/snort3:wizard to master

Squashed commit of the following:

commit ad41e68e63256944ec6a6ffb1d1074f2fd891250
Author: maya dagon <mdagon@cisco.com>
Date:   Mon Jul 31 14:51:01 2023 -0400

    wizard: refactoring - split curses to multiple files by protocol

14 files changed:
src/service_inspectors/wizard/CMakeLists.txt
src/service_inspectors/wizard/curse_book.cc [new file with mode: 0644]
src/service_inspectors/wizard/curse_book.h [moved from src/service_inspectors/wizard/curses.h with 50% similarity]
src/service_inspectors/wizard/curses.cc [deleted file]
src/service_inspectors/wizard/dce_curse.cc [new file with mode: 0644]
src/service_inspectors/wizard/dce_curse.h [new file with mode: 0644]
src/service_inspectors/wizard/mms_curse.cc [new file with mode: 0644]
src/service_inspectors/wizard/mms_curse.h
src/service_inspectors/wizard/s7commplus_curse.cc [new file with mode: 0644]
src/service_inspectors/wizard/s7commplus_curse.h
src/service_inspectors/wizard/ssl_curse.cc [new file with mode: 0644]
src/service_inspectors/wizard/ssl_curse.h [new file with mode: 0644]
src/service_inspectors/wizard/wiz_module.cc
src/service_inspectors/wizard/wizard.cc

index 33ab8c8033de0139b2a9cf6c998575ea06557634..90f3b061160ebd6b706a35715eba31f5a04d3f06 100644 (file)
@@ -1,11 +1,17 @@
 
 set(FILE_LIST
-    curses.cc
-    curses.h
+    curse_book.cc
+    curse_book.h
+    dce_curse.cc
+    dce_curse.h
     magic.cc
     magic.h
+    mms_curse.cc
     mms_curse.h
+    s7commplus_curse.cc
     s7commplus_curse.h
+    ssl_curse.cc
+    ssl_curse.h
     hexes.cc
     spells.cc
     wizard.cc
@@ -24,5 +30,5 @@ endif (STATIC_INSPECTORS)
 add_catch_test(curses_test
     NO_TEST_SOURCE
     SOURCES
-        curses.cc
+        ssl_curse.cc
 )
diff --git a/src/service_inspectors/wizard/curse_book.cc b/src/service_inspectors/wizard/curse_book.cc
new file mode 100644 (file)
index 0000000..7219337
--- /dev/null
@@ -0,0 +1,62 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2023-2023 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.
+//--------------------------------------------------------------------------
+// curse_book.cc author Maya Dagon <mdagon@cisco.com>
+// Based on curses.cc
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "curse_book.h"
+
+using namespace std;
+
+// map between service and curse details
+vector<CurseDetails> CurseBook::curse_map = 
+{
+    // name         service        alg                          is_tcp
+    { "dce_udp"   , "dcerpc"     , CurseBook::dce_udp_curse   , false },
+    { "dce_tcp"   , "dcerpc"     , CurseBook::dce_tcp_curse   , true  },
+    { "mms"       , "mms"        , CurseBook::mms_curse       , true  },
+    { "s7commplus", "s7commplus" , CurseBook::s7commplus_curse, true  },
+    { "dce_smb"   , "netbios-ssn", CurseBook::dce_smb_curse   , true  },
+    { "sslv2"     , "ssl"        , CurseBook::ssl_v2_curse    , true  }
+};
+
+bool CurseBook::add_curse(const char* key)
+{
+    for ( const CurseDetails& curse : curse_map )
+    {
+        if ( curse.name == key )
+        {
+            if ( curse.is_tcp )
+                tcp_curses.emplace_back(&curse);
+            else
+                non_tcp_curses.emplace_back(&curse);
+
+            return true;
+        }
+    }
+
+    return false;
+}
+
+const vector<const CurseDetails*>& CurseBook::get_curses(bool tcp) const
+{
+    return tcp ? tcp_curses : non_tcp_curses;
+}
similarity index 50%
rename from src/service_inspectors/wizard/curses.h
rename to src/service_inspectors/wizard/curse_book.h
index 1a0764d0f6d07a6855dc95905bb47983a9a53058..8960c4bc8607823576a938be9ca9f20daf314cfb 100644 (file)
@@ -1,5 +1,5 @@
 //--------------------------------------------------------------------------
-// Copyright (C) 2016-2023 Cisco and/or its affiliates. All rights reserved.
+// Copyright (C) 2023-2023 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
 // with this program; if not, write to the Free Software Foundation, Inc.,
 // 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 //--------------------------------------------------------------------------
-// curses.h author Maya Dagon <mdagon@cisco.com>
+// curse_book.h author Maya Dagon <mdagon@cisco.com>
+// Refactored from curse.h
 
-#ifndef CURSES_H
-#define CURSES_H
+#ifndef CURSE_BOOK_H
+#define CURSE_BOOK_H
 
 #include <cstdint>
 #include <string>
 #include <vector>
 
+#include "dce_curse.h"
 #include "mms_curse.h"
 #include "s7commplus_curse.h"
-
-enum DCE_State
-{
-    STATE_0 = 0,
-    STATE_1,
-    STATE_2,
-    STATE_3,
-    STATE_4,
-    STATE_5,
-    STATE_6,
-    STATE_7,
-    STATE_8,
-    STATE_9,
-    STATE_10
-};
-
-enum SSL_State
-{
-    BYTE_0_LEN_MSB = 0,
-    BYTE_1_LEN_LSB,
-    BYTE_2_CLIENT_HELLO,
-    BYTE_3_MAX_MINOR_VER,
-    BYTE_4_V3_MAJOR,
-    BYTE_5_SPECS_LEN_MSB,
-    BYTE_6_SPECS_LEN_LSB,
-    BYTE_7_SSNID_LEN_MSB,
-    BYTE_8_SSNID_LEN_LSB,
-    BYTE_9_CHLNG_LEN_MSB,
-    BYTE_10_CHLNG_LEN_LSB,
-    SSL_FOUND,
-    SSL_NOT_FOUND
-};
+#include "ssl_curse.h"
 
 class CurseTracker
 {
 public:
-    struct DCE
-    {
-        DCE_State state;
-        uint32_t helper;
-    } dce;
-
-    struct MMS
-    {
-        MMS_State state;
-        MMS_State last_state;
-    } mms;
-
-    struct S7COMMPLUS
-    {
-        S7commplus_State state;
-        S7commplus_State last_state;
-        uint16_t func;
-    } s7commplus;
-
-    struct SSL
-    {
-        SSL_State state;
-        unsigned total_len;
-        unsigned ssnid_len;
-        unsigned specs_len;
-        unsigned chlng_len;
-    } ssl;
-
-    CurseTracker()
-    {
-        dce.state = DCE_State::STATE_0;
-        mms.state = MMS_State::MMS_STATE__TPKT_VER;
-        mms.last_state = mms.state;
-        s7commplus.state = S7commplus_State::S7COMMPLUS_STATE__TPKT_VER;
-        s7commplus.last_state = s7commplus.state;
-        s7commplus.func = 0;
-        ssl.state = SSL_State::BYTE_0_LEN_MSB;
-    }
+    DceTracker dce;
+    MmsTracker mms;
+    S7commplusTracker s7commplus;
+    SslTracker ssl;
 };
 
 typedef bool (* curse_alg)(const uint8_t* data, unsigned len, CurseTracker*);
@@ -121,7 +58,17 @@ public:
 private:
     std::vector<const CurseDetails*> tcp_curses;
     std::vector<const CurseDetails*> non_tcp_curses;
+    static std::vector<CurseDetails> curse_map;
+
+    static bool dce_udp_curse(const uint8_t* data, unsigned len, CurseTracker*);
+    static bool dce_tcp_curse(const uint8_t* data, unsigned len, CurseTracker*);
+    static bool dce_smb_curse(const uint8_t* data, unsigned len, CurseTracker*);
+    static bool mms_curse(const uint8_t* data, unsigned len, CurseTracker*);
+    static bool s7commplus_curse(const uint8_t* data, unsigned len, CurseTracker*);
+#ifdef CATCH_TEST_BUILD
+public:
+#endif  
+    static bool ssl_v2_curse(const uint8_t* data, unsigned len, CurseTracker*);
 };
 
 #endif
-
diff --git a/src/service_inspectors/wizard/curses.cc b/src/service_inspectors/wizard/curses.cc
deleted file mode 100644 (file)
index e91708a..0000000
+++ /dev/null
@@ -1,1040 +0,0 @@
-//--------------------------------------------------------------------------
-// Copyright (C) 2016-2023 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.
-//--------------------------------------------------------------------------
-// curses.cc author Maya Dagon <mdagon@cisco.com>
-// mms_curse and s7commplus_curse author Jared Rittle <jared.rittle@cisco.com>
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "curses.h"
-
-#include <assert.h>
-
-using namespace std;
-
-enum DceRpcPduType
-{
-    DCERPC_PDU_TYPE__REQUEST = 0,
-    DCERPC_PDU_TYPE__PING,
-    DCERPC_PDU_TYPE__RESPONSE,
-    DCERPC_PDU_TYPE__FAULT,
-    DCERPC_PDU_TYPE__WORKING,
-    DCERPC_PDU_TYPE__NOCALL,
-    DCERPC_PDU_TYPE__REJECT,
-    DCERPC_PDU_TYPE__ACK,
-    DCERPC_PDU_TYPE__CL_CANCEL,
-    DCERPC_PDU_TYPE__FACK,
-    DCERPC_PDU_TYPE__CANCEL_ACK,
-    DCERPC_PDU_TYPE__BIND,
-    DCERPC_PDU_TYPE__BIND_ACK,
-    DCERPC_PDU_TYPE__BIND_NACK,
-    DCERPC_PDU_TYPE__ALTER_CONTEXT,
-    DCERPC_PDU_TYPE__ALTER_CONTEXT_RESP,
-    DCERPC_PDU_TYPE__AUTH3,
-    DCERPC_PDU_TYPE__SHUTDOWN,
-    DCERPC_PDU_TYPE__CO_CANCEL,
-    DCERPC_PDU_TYPE__ORPHANED,
-    DCERPC_PDU_TYPE__MICROSOFT_PROPRIETARY_OUTLOOK2003_RPC_OVER_HTTP,
-    DCERPC_PDU_TYPE__MAX
-};
-
-/* Version 4 is for Connectionless
- * Version 5 is for Connection oriented */
-enum DceRpcProtoMajorVers
-{
-    DCERPC_PROTO_MAJOR_VERS__4 = 4,
-    DCERPC_PROTO_MAJOR_VERS__5 = 5
-};
-
-enum DceRpcProtoMinorVers
-{
-    DCERPC_PROTO_MINOR_VERS__0 = 0,
-    DCERPC_PROTO_MINOR_VERS__1 = 1
-};
-
-static bool dce_udp_curse(const uint8_t* data, unsigned len, CurseTracker*)
-{
-    const uint8_t dcerpc_cl_hdr_len = 80;
-    const uint8_t cl_len_offset = 74;
-
-    if ( len >= dcerpc_cl_hdr_len )
-    {
-        uint8_t version = data[0];
-        uint8_t pdu_type = data[1];
-        bool little_endian = ((data[4] & 0x10) >> 4) ? true : false;
-        uint16_t cl_len;
-
-#ifdef WORDS_BIGENDIAN
-        if ( !little_endian )
-#else
-        if ( little_endian )
-#endif  /* WORDS_BIGENDIAN */
-            cl_len = (data[cl_len_offset+1] << 8) | data[cl_len_offset];
-        else
-            cl_len = (data[cl_len_offset] << 8) | data[cl_len_offset+1];
-
-        if ( (version == DCERPC_PROTO_MAJOR_VERS__4) and
-            ((pdu_type == DCERPC_PDU_TYPE__REQUEST) or
-            (pdu_type == DCERPC_PDU_TYPE__RESPONSE) or
-            (pdu_type == DCERPC_PDU_TYPE__FAULT) or
-            (pdu_type == DCERPC_PDU_TYPE__REJECT) or
-            (pdu_type == DCERPC_PDU_TYPE__FACK)) and
-            ((cl_len != 0) and
-            (cl_len + (unsigned)dcerpc_cl_hdr_len) <= len) )
-            return true;
-    }
-
-    return false;
-}
-
-static bool dce_tcp_curse(const uint8_t* data, unsigned len, CurseTracker* tracker)
-{
-    const uint8_t dce_rpc_co_hdr_len = 16;
-    CurseTracker::DCE& dce = tracker->dce;
-
-    uint32_t n = 0;
-    while ( n < len )
-    {
-        switch ( dce.state )
-        {
-        case STATE_0: // check major version
-            if ( data[n] != DCERPC_PROTO_MAJOR_VERS__5 )
-            {
-                // go to bad state
-                dce.state = STATE_10;
-
-                return false;
-            }
-
-            dce.state = (DCE_State)((int)dce.state + 1);
-            break;
-
-        case STATE_1: // check minor version
-            if ( data[n] != DCERPC_PROTO_MINOR_VERS__0 )
-            {
-                // go to bad state
-                dce.state = STATE_10;
-
-                return false;
-            }
-
-            dce.state = (DCE_State)((int)dce.state + 1);
-            break;
-
-        case STATE_2: // pdu_type
-        {
-            uint8_t pdu_type = data[n];
-
-            if ( (pdu_type != DCERPC_PDU_TYPE__BIND) and
-                (pdu_type != DCERPC_PDU_TYPE__BIND_ACK) )
-            {
-                // go to bad state
-                dce.state = STATE_10;
-
-                return false;
-            }
-
-            dce.state = (DCE_State)((int)dce.state + 1);
-            break;
-        }
-
-        case STATE_4: //little endian
-            dce.helper = (data[n] & 0x10) << 20;
-            dce.state = (DCE_State)((int)dce.state + 1);
-            break;
-        case STATE_8:
-            dce.helper |= data[n];
-            dce.state = (DCE_State)((int)dce.state + 1);
-            break;
-        case STATE_9:
-#ifdef WORDS_BIGENDIAN
-            if ( !(dce.helper >> 24) )
-#else
-            if ( dce.helper >> 24 )
-#endif  /* WORDS_BIGENDIAN */
-                dce.helper = (data[n] << 8) | (dce.helper & 0XFF);
-            else
-            {
-                dce.helper <<=8;
-                dce.helper |= data[n];
-            }
-
-            if ( dce.helper >= dce_rpc_co_hdr_len )
-                return true;
-
-            dce.state = STATE_10;
-            break;
-
-        case STATE_10:
-            // no match
-            return false;
-        default:
-            dce.state = (DCE_State)((int)dce.state + 1);
-            break;
-        }
-
-        n++;
-    }
-
-    return false;
-}
-
-static bool dce_smb_curse(const uint8_t* data, unsigned len, CurseTracker* tracker)
-{
-    const uint32_t dce_smb_id = 0xff534d42;  /* \xffSMB */
-    const uint32_t dce_smb2_id = 0xfe534d42;  /* \xfeSMB */
-    const uint8_t session_request = 0x81, session_response = 0x82, session_message = 0x00;
-    CurseTracker::DCE& dce = tracker->dce;
-
-    uint32_t n = 0;
-    while ( n < len )
-    {
-        switch ( dce.state )
-        {
-        case STATE_0:
-            if ( data[n] == session_message )
-            {
-                dce.state = (DCE_State)((int)dce.state + 2);
-                break;
-            }
-
-            if ( data[n] == session_request or data[n] == session_response )
-            {
-                dce.state = (DCE_State)((int)dce.state + 1);
-
-                return false;
-            }
-
-            dce.state = STATE_9;
-
-            return false;
-
-        case STATE_1:
-            if ( data[n] == session_message )
-            {
-                dce.state = (DCE_State)((int)dce.state + 1);
-                break;
-            }
-
-            dce.state = STATE_9;
-
-            return false;
-
-        case STATE_5:
-            dce.helper = data[n];
-            dce.state = (DCE_State)((int)dce.state + 1);
-            break;
-
-        case STATE_6:
-        case STATE_7:
-            dce.helper <<= 8;
-            dce.helper |= data[n];
-            dce.state = (DCE_State)((int)dce.state + 1);
-            break;
-
-        case STATE_8:
-            dce.helper <<= 8;
-            dce.helper |= data[n];
-
-            if ( (dce.helper == dce_smb_id) or (dce.helper == dce_smb2_id) )
-                return true;
-
-            dce.state = (DCE_State)((int)dce.state + 1);
-            break;
-
-        case STATE_9:
-            // no match
-            return false;
-
-        default:
-            dce.state = (DCE_State)((int)dce.state + 1);
-            break;
-        }
-
-        n++;
-    }
-
-    return false;
-}
-
-
-static bool mms_curse(const uint8_t* data, unsigned len, CurseTracker* tracker)
-{
-    // peg the tracker to MMS
-    CurseTracker::MMS& mms = tracker->mms;
-
-    // if the state is set to MMS_STATE__SEARCH it means we most likely
-    // have a split pipelined message coming through and will need to
-    // reset the state
-    if ( mms.state == MMS_STATE__SEARCH )
-    {
-        mms.state = mms.last_state;
-    }
-
-    // define all known MMS tags to check for
-    enum
-    {
-        MMS_CONFIRMED_REQUEST_TAG    = 0xA0,
-        MMS_CONFIRMED_RESPONSE_TAG   = 0xA1,
-        MMS_CONFIRMED_ERROR_TAG      = 0xA2,
-        MMS_UNCONFIRMED_TAG          = 0xA3,
-        MMS_REJECT_TAG               = 0xA4,
-        MMS_CANCEL_REQUEST_TAG       = 0x85,
-        MMS_CANCEL_RESPONSE_TAG      = 0x86,
-        MMS_CANCEL_ERROR_TAG         = 0xA7,
-        MMS_INITIATE_REQUEST_TAG     = 0xA8,
-        MMS_INITIATE_RESPONSE_TAG    = 0xA9,
-        MMS_INITIATE_ERROR_TAG       = 0xAA,
-        MMS_CONCLUDE_REQUEST_TAG     = 0x8B,
-        MMS_CONCLUDE_RESPONSE_TAG    = 0x8C,
-        MMS_CONCLUDE_ERROR_TAG       = 0xAD,
-    };
-
-    uint32_t idx = 0;
-    while ( idx < len )
-    {
-        switch ( mms.state )
-        {
-            case MMS_STATE__TPKT_VER:
-            {
-                mms.state = MMS_STATE__TPKT_RES;
-                break;
-            }
-
-            case MMS_STATE__TPKT_RES:
-            {
-                mms.state = MMS_STATE__TPKT_LEN1;
-                break;
-            }
-
-            case MMS_STATE__TPKT_LEN1:
-            {
-                mms.state = MMS_STATE__TPKT_LEN2;
-                break;
-            }
-
-            case MMS_STATE__TPKT_LEN2:
-            {
-                mms.state = MMS_STATE__COTP_LEN;
-                break;
-            }
-
-            case MMS_STATE__COTP_LEN:
-            {
-                mms.state = MMS_STATE__COTP_PDU;
-                break;
-            }
-
-            case MMS_STATE__COTP_PDU:
-            {
-                // 7 6 5 4 3 2 1 0
-                // ---------------
-                // . . . . x x x x   Destination Reference
-                // x x x x . . . .   PDU Type
-                const uint32_t MMS_COTP_PDU_DT_DATA = 0x0F;
-
-                if ( data[idx] >> 0x04 != MMS_COTP_PDU_DT_DATA )
-                {
-                    mms.state = MMS_STATE__NOT_FOUND;
-                    break;
-                }
-
-                mms.state = MMS_STATE__COTP_TPDU_NUM;
-                break;
-            }
-
-            case MMS_STATE__COTP_TPDU_NUM:
-            {
-                mms.state = MMS_STATE__OSI_SESSION_SPDU;
-                break;
-            }
-
-            case MMS_STATE__OSI_SESSION_SPDU:
-            {
-                // define all known OSI Session layer SPDU tags to check
-                enum
-                {
-                    MMS_OSI_SESSION_SPDU_GT_DT = 0x01,
-                    MMS_OSI_SESSION_SPDU_CN = 0x0D,
-                    MMS_OSI_SESSION_SPDU_AC = 0x0E,
-                };
-
-                switch ( data[idx] )
-                {
-                    // check for a known MMS message tag in the event Session/Pres/ACSE aren't used
-                    case MMS_CONFIRMED_REQUEST_TAG:    // fallthrough intentional
-                    case MMS_CONFIRMED_RESPONSE_TAG:   // fallthrough intentional
-                    case MMS_CONFIRMED_ERROR_TAG:      // fallthrough intentional
-                    case MMS_UNCONFIRMED_TAG:          // fallthrough intentional
-                    case MMS_REJECT_TAG:               // fallthrough intentional
-                    case MMS_CANCEL_REQUEST_TAG:       // fallthrough intentional
-                    case MMS_CANCEL_RESPONSE_TAG:      // fallthrough intentional
-                    case MMS_CANCEL_ERROR_TAG:         // fallthrough intentional
-                    case MMS_INITIATE_REQUEST_TAG:     // fallthrough intentional
-                    case MMS_INITIATE_RESPONSE_TAG:    // fallthrough intentional
-                    case MMS_INITIATE_ERROR_TAG:       // fallthrough intentional
-                    case MMS_CONCLUDE_REQUEST_TAG:     // fallthrough intentional
-                    case MMS_CONCLUDE_RESPONSE_TAG:    // fallthrough intentional
-                    case MMS_CONCLUDE_ERROR_TAG:
-                    {
-                        // if an MMS tag exists in the remaining data,
-                        // hand off to the MMS service inspector
-                        mms.state = MMS_STATE__FOUND;
-                        break;
-                    }
-
-                    // if mms isn't found, search for an OSI Session layer
-                    case MMS_OSI_SESSION_SPDU_GT_DT: // fallthrough intentional
-                    case MMS_OSI_SESSION_SPDU_CN:    // fallthrough intentional
-                    case MMS_OSI_SESSION_SPDU_AC:
-                    {
-                        mms.state = MMS_STATE__MMS;
-                        break;
-                    }
-
-                    // if neither are found, it is most likely not MMS
-                    default:
-                    {
-                        mms.state = MMS_STATE__NOT_FOUND;
-                    }
-                }
-
-                break;
-            }
-
-            case MMS_STATE__MMS:
-            {
-                // loop through the remaining bytes in the buffer checking for known MMS tags
-                for ( uint32_t i=idx; i < len; i++ )
-                {
-                    // for each remaining byte check to see if it is in the known tag map
-                    switch ( data[i] )
-                    {
-                        case MMS_CONFIRMED_REQUEST_TAG:    // fallthrough intentional
-                        case MMS_CONFIRMED_RESPONSE_TAG:   // fallthrough intentional
-                        case MMS_CONFIRMED_ERROR_TAG:      // fallthrough intentional
-                        case MMS_UNCONFIRMED_TAG:          // fallthrough intentional
-                        case MMS_REJECT_TAG:               // fallthrough intentional
-                        case MMS_CANCEL_REQUEST_TAG:       // fallthrough intentional
-                        case MMS_CANCEL_RESPONSE_TAG:      // fallthrough intentional
-                        case MMS_CANCEL_ERROR_TAG:         // fallthrough intentional
-                        case MMS_INITIATE_REQUEST_TAG:     // fallthrough intentional
-                        case MMS_INITIATE_RESPONSE_TAG:    // fallthrough intentional
-                        case MMS_INITIATE_ERROR_TAG:       // fallthrough intentional
-                        case MMS_CONCLUDE_REQUEST_TAG:     // fallthrough intentional
-                        case MMS_CONCLUDE_RESPONSE_TAG:    // fallthrough intentional
-                        case MMS_CONCLUDE_ERROR_TAG:
-                        {
-                            // if an MMS tag exists in the remaining data,
-                            // hand off to the MMS service inspector
-                            mms.state = MMS_STATE__FOUND;
-                            break;
-                        }
-                        // no default here as it we don't know when we would hit
-                        // the first MMS tag without doing full parsing
-                    }
-
-                    // exit the loop when a state has been determined
-                    if ( mms.state == MMS_STATE__NOT_FOUND
-                        or mms.state == MMS_STATE__SEARCH
-                        or mms.state == MMS_STATE__FOUND )
-                    {
-                        break;
-                    }
-                }
-
-                break;
-            }
-
-            case MMS_STATE__FOUND:
-            {
-                mms.state = MMS_STATE__TPKT_VER;
-
-                return true;
-            }
-
-            case MMS_STATE__NOT_FOUND:
-            {
-                mms.state = MMS_STATE__TPKT_VER;
-
-                return false;
-            }
-
-            default:
-            {
-                mms.state = MMS_STATE__NOT_FOUND;
-                assert(false);
-                break;
-            }
-        }
-
-        idx++;
-    }
-
-    mms.last_state = mms.state;
-    mms.state = MMS_STATE__SEARCH;
-
-    return false;
-}
-
-
-static bool s7commplus_curse(const uint8_t* data, unsigned len, CurseTracker* tracker)
-{
-    // peg the tracker to s7commplus
-    CurseTracker::S7COMMPLUS& s7commplus = tracker->s7commplus;
-
-    // if the state is set to S7COMMPLUS_STATE__SEARCH it means we most likely
-    // have a split pipelined message coming through and will need to
-    // reset the state
-    if ( s7commplus.state == S7COMMPLUS_STATE__SEARCH )
-    {
-        s7commplus.state = s7commplus.last_state;
-    }
-
-    uint32_t idx = 0;
-    while ( idx < len )
-    {
-        switch ( s7commplus.state )
-        {
-            case S7COMMPLUS_STATE__TPKT_VER:
-            {
-                s7commplus.state = S7COMMPLUS_STATE__TPKT_RES;
-                break;
-            }
-
-            case S7COMMPLUS_STATE__TPKT_RES:
-            {
-                s7commplus.state = S7COMMPLUS_STATE__TPKT_LEN1;
-                break;
-            }
-
-            case S7COMMPLUS_STATE__TPKT_LEN1:
-            {
-                s7commplus.state = S7COMMPLUS_STATE__TPKT_LEN2;
-                break;
-            }
-
-            case S7COMMPLUS_STATE__TPKT_LEN2:
-            {
-                s7commplus.state = S7COMMPLUS_STATE__COTP_LEN;
-                break;
-            }
-
-            case S7COMMPLUS_STATE__COTP_LEN:
-            {
-                s7commplus.state = S7COMMPLUS_STATE__COTP_PDU;
-                break;
-            }
-
-            case S7COMMPLUS_STATE__COTP_PDU:
-            {
-                // 7 6 5 4 3 2 1 0
-                // ---------------
-                // . . . . x x x x   Destination Reference
-                // x x x x . . . .   PDU Type
-                const uint32_t S7COMMPLUS_COTP_PDU_DT_DATA = 0x0F;
-
-                if ( data[idx] >> 0x04 != S7COMMPLUS_COTP_PDU_DT_DATA )
-                {
-                    s7commplus.state = S7COMMPLUS_STATE__NOT_FOUND;
-                    break;
-                }
-
-                s7commplus.state = S7COMMPLUS_STATE__COTP_TPDU_NUM;
-                break;
-            }
-
-            case S7COMMPLUS_STATE__COTP_TPDU_NUM:
-            {
-                s7commplus.state = S7COMMPLUS_STATE__PROTO_ID;
-                break;
-            }
-
-            case S7COMMPLUS_STATE__PROTO_ID:
-            {
-                // there are two possible protocol identifiers - 0x32 and 0x72
-                // 0x32 indicates the original s7comm protocol
-                //   * the original protocol is not supported within the inspector
-                //     so just catching and considering it a no match for now
-                // 0x72 indicates the s7commplus protocol
-                //   * this is the protocol on which the existing inspector focuses
-                if ( data[idx] == S7COMMPLUS_PROTOCOL_IDENTIFIER__S7COMMPLUS )
-                {
-                    s7commplus.state = S7COMMPLUS_STATE__PDU_TYPE;
-                }
-                else
-                {
-                    s7commplus.state = S7COMMPLUS_STATE__NOT_FOUND;
-                }
-                break;
-            }
-
-            case S7COMMPLUS_STATE__PDU_TYPE:
-            {
-                switch ( data[idx] )
-                {
-                    case S7COMMPLUS_PDU_TYPE__CONNECT:      // fallthrough intentional
-                    case S7COMMPLUS_PDU_TYPE__DATA:         // fallthrough intentional
-                    case S7COMMPLUS_PDU_TYPE__DATA2:        // fallthrough intentional
-                    case S7COMMPLUS_PDU_TYPE__KEEPALIVE:
-                    {
-                        s7commplus.state = S7COMMPLUS_STATE__DATALENGTH_1;
-                        break;
-                    }
-
-                    default:
-                    {
-                        s7commplus.state = S7COMMPLUS_STATE__NOT_FOUND;
-                        break;
-                    }
-                }
-
-                break;
-            }
-
-            case S7COMMPLUS_STATE__DATALENGTH_1:
-            {
-                s7commplus.state = S7COMMPLUS_STATE__DATALENGTH_2;
-                break;
-            }
-
-            case S7COMMPLUS_STATE__DATALENGTH_2:
-            {
-                s7commplus.state = S7COMMPLUS_STATE__OPCODE;
-                break;
-            }
-
-            case S7COMMPLUS_STATE__OPCODE:
-            {
-                switch ( data[idx] )
-                {
-                    case S7COMMPLUS_OPCODE__REQ:               // fallthrough intentional
-                    case S7COMMPLUS_OPCODE__RES:               // fallthrough intentional
-                    case S7COMMPLUS_OPCODE__NOTIFICATION:      // fallthrough intentional
-                    case S7COMMPLUS_OPCODE__RES2:
-                    {
-                        s7commplus.state = S7COMMPLUS_STATE__RES_1;
-                        break;
-                    }
-
-                    default:
-                    {
-                        s7commplus.state = S7COMMPLUS_STATE__NOT_FOUND;
-                        break;
-                    }
-                }
-
-                break;
-            }
-
-            case S7COMMPLUS_STATE__RES_1:
-            {
-                s7commplus.state = S7COMMPLUS_STATE__RES_2;
-                break;
-            }
-
-            case S7COMMPLUS_STATE__RES_2:
-            {
-                s7commplus.state = S7COMMPLUS_STATE__FUNCTION_1;
-                break;
-            }
-
-            case S7COMMPLUS_STATE__FUNCTION_1:
-            {
-                // make sure the function code is zeroed out before building
-                s7commplus.func = 0;
-
-                // get the high byte of the function code
-                s7commplus.func = data[idx] << 0x08;
-
-                // move on to the low byte
-                s7commplus.state = S7COMMPLUS_STATE__FUNCTION_2;
-                break;
-            }
-
-            case S7COMMPLUS_STATE__FUNCTION_2:
-            {
-                // get the low byte of the function code
-                s7commplus.func |= data[idx];
-
-                switch ( s7commplus.func )
-                {
-                    case S7COMMPLUS_FUNCTION__EXPLORE:               // fallthrough intentional
-                    case S7COMMPLUS_FUNCTION__CREATEOBJECT:          // fallthrough intentional
-                    case S7COMMPLUS_FUNCTION__DELETEOBJECT:          // fallthrough intentional
-                    case S7COMMPLUS_FUNCTION__SETVARIABLE:           // fallthrough intentional
-                    case S7COMMPLUS_FUNCTION__GETLINK:               // fallthrough intentional
-                    case S7COMMPLUS_FUNCTION__SETMULTIVAR:           // fallthrough intentional
-                    case S7COMMPLUS_FUNCTION__GETMULTIVAR:           // fallthrough intentional
-                    case S7COMMPLUS_FUNCTION__BEGINSEQUENCE:         // fallthrough intentional
-                    case S7COMMPLUS_FUNCTION__ENDSEQUENCE:           // fallthrough intentional
-                    case S7COMMPLUS_FUNCTION__INVOKE:                // fallthrough intentional
-                    case S7COMMPLUS_FUNCTION__GETVARSUBSTR:
-                    {
-                        s7commplus.state = S7COMMPLUS_STATE__FOUND;
-                        break;
-                    }
-
-                    default:
-                    {
-                        s7commplus.state = S7COMMPLUS_STATE__NOT_FOUND;
-                        break;
-                    }
-                }
-
-                break;
-            }
-
-            case S7COMMPLUS_STATE__FOUND:
-            {
-                s7commplus.state = S7COMMPLUS_STATE__TPKT_VER;
-
-                return true;
-            }
-
-            case S7COMMPLUS_STATE__NOT_FOUND:
-            {
-                s7commplus.state = S7COMMPLUS_STATE__TPKT_VER;
-
-                return false;
-            }
-
-            default:
-            {
-                s7commplus.state = S7COMMPLUS_STATE__NOT_FOUND;
-                assert(false);
-                break;
-            }
-        }
-
-        idx++;
-    }
-
-    s7commplus.last_state = s7commplus.state;
-    s7commplus.state = S7COMMPLUS_STATE__SEARCH;
-
-    return false;
-}
-
-
-namespace SSL_Const
-{
-static constexpr uint8_t hdr_len = 9;
-static constexpr uint8_t sslv2_msb_set = 0x80;
-static constexpr uint8_t client_hello = 0x01;
-static constexpr uint8_t sslv3_major_ver = 0x03;
-static constexpr uint8_t sslv3_max_minor_ver = 0x03;
-}
-
-static bool ssl_v2_curse(const uint8_t* data, unsigned len, CurseTracker* tracker)
-{
-    CurseTracker::SSL& ssl = tracker->ssl;
-
-    if ( ssl.state == SSL_State::SSL_NOT_FOUND )
-        return false;
-    else if ( ssl.state == SSL_State::SSL_FOUND )
-        return true;
-
-    for ( unsigned i = 0; i < len; ++i )
-    {
-        uint8_t val = data[i];
-
-        switch ( ssl.state )
-        {
-        case SSL_State::BYTE_0_LEN_MSB:
-            if ( (val & SSL_Const::sslv2_msb_set) == 0 )
-            {
-                ssl.state = SSL_State::SSL_NOT_FOUND;
-
-                return false;
-            }
-
-            ssl.total_len = (val & (~SSL_Const::sslv2_msb_set)) << 8;
-            ssl.state = SSL_State::BYTE_1_LEN_LSB;
-            break;
-
-        case SSL_State::BYTE_1_LEN_LSB:
-            ssl.total_len |= val;
-            if ( ssl.total_len < SSL_Const::hdr_len )
-            {
-                ssl.state = SSL_State::SSL_NOT_FOUND;
-
-                return false;
-            }
-
-            ssl.total_len -= SSL_Const::hdr_len;
-            ssl.state = SSL_State::BYTE_2_CLIENT_HELLO;
-            break;
-
-        case SSL_State::BYTE_2_CLIENT_HELLO:
-            if ( val != SSL_Const::client_hello )
-            {
-                ssl.state = SSL_State::SSL_NOT_FOUND;
-
-                return false;
-            }
-
-            ssl.state = SSL_State::BYTE_3_MAX_MINOR_VER;
-            break;
-
-        case SSL_State::BYTE_3_MAX_MINOR_VER:
-            if ( val > SSL_Const::sslv3_max_minor_ver )
-            {
-                ssl.state = SSL_State::SSL_NOT_FOUND;
-
-                return false;
-            }
-
-            ssl.state = SSL_State::BYTE_4_V3_MAJOR;
-            break;
-
-        case SSL_State::BYTE_4_V3_MAJOR:
-            if ( val > SSL_Const::sslv3_major_ver )
-            {
-                ssl.state = SSL_State::SSL_NOT_FOUND;
-
-                return false;
-            }
-
-            ssl.state = SSL_State::BYTE_5_SPECS_LEN_MSB;
-            break;
-
-        case SSL_State::BYTE_5_SPECS_LEN_MSB:
-            ssl.specs_len = val << 8;
-            ssl.state = SSL_State::BYTE_6_SPECS_LEN_LSB;
-            break;
-
-        case SSL_State::BYTE_6_SPECS_LEN_LSB:
-            ssl.specs_len |= val;
-
-            if ( ssl.total_len < ssl.specs_len )
-            {
-                ssl.state = SSL_State::SSL_NOT_FOUND;
-
-                return false;
-            }
-
-            ssl.total_len -= ssl.specs_len;
-            ssl.state = SSL_State::BYTE_7_SSNID_LEN_MSB;
-            break;
-
-        case SSL_State::BYTE_7_SSNID_LEN_MSB:
-            ssl.ssnid_len = val << 8;
-            ssl.state = SSL_State::BYTE_8_SSNID_LEN_LSB;
-            break;
-
-        case SSL_State::BYTE_8_SSNID_LEN_LSB:
-            ssl.ssnid_len |= val;
-
-            if ( ssl.total_len < ssl.ssnid_len )
-            {
-                ssl.state = SSL_State::SSL_NOT_FOUND;
-
-                return false;
-            }
-
-            ssl.total_len -= ssl.ssnid_len;
-            ssl.state = SSL_State::BYTE_9_CHLNG_LEN_MSB;
-            break;
-
-        case SSL_State::BYTE_9_CHLNG_LEN_MSB:
-            ssl.chlng_len = val << 8;
-            ssl.state = SSL_State::BYTE_10_CHLNG_LEN_LSB;
-            break;
-
-        case SSL_State::BYTE_10_CHLNG_LEN_LSB:
-            ssl.chlng_len |= val;
-
-            if ( ssl.total_len < ssl.chlng_len )
-            {
-                ssl.state = SSL_State::SSL_NOT_FOUND;
-
-                return false;
-            }
-
-            ssl.state = SSL_State::SSL_FOUND;
-
-            return true;
-
-        default:
-            return false;
-        }
-    }
-
-    return false;
-}
-
-
-// map between service and curse details
-static vector<CurseDetails> curse_map
-{
-    // name         service        alg               is_tcp
-    { "dce_udp"   , "dcerpc"     , dce_udp_curse   , false },
-    { "dce_tcp"   , "dcerpc"     , dce_tcp_curse   , true  },
-    { "mms"       , "mms"        , mms_curse       , true  },
-    { "s7commplus", "s7commplus" , s7commplus_curse, true  },
-    { "dce_smb"   , "netbios-ssn", dce_smb_curse   , true  },
-    { "sslv2"     , "ssl"        , ssl_v2_curse    , true  }
-};
-
-bool CurseBook::add_curse(const char* key)
-{
-    for ( const CurseDetails& curse : curse_map )
-    {
-        if ( curse.name == key )
-        {
-            if ( curse.is_tcp )
-                tcp_curses.emplace_back(&curse);
-            else
-                non_tcp_curses.emplace_back(&curse);
-
-            return true;
-        }
-    }
-
-    return false;
-}
-
-const vector<const CurseDetails*>& CurseBook::get_curses(bool tcp) const
-{
-    if ( tcp )
-        return tcp_curses;
-
-    return non_tcp_curses;
-}
-
-#ifdef CATCH_TEST_BUILD
-
-#include "catch/catch.hpp"
-#include <cstring>
-
-//client hello with v2 header advertising sslv2
-static const uint8_t ssl_v2_ch[] =
-{ 0x80,0x59,0x01,0x00,0x02,0x00,0x30,0x00,0x00,0x00,0x20,0x00,0x00,0x39,0x00,0x00,
-  0x38,0x00,0x00,0x35,0x00,0x00,0x16,0x00,0x00,0x13,0x00,0x00,0x0a,0x00,0x00,0x33,
-  0x00,0x00,0x32,0x00,0x00,0x2f,0x00,0x00,0x07,0x00,0x00,0x05,0x00,0x00,0x04,0x00,
-  0x00,0x15,0x00,0x00,0x12,0x00,0x00,0x09,0x00,0x00,0xff,0xda,0x86,0xfa,0xb4,0x73,
-  0x5a,0x1e,0x11,0xd1,0xdb,0x58,0x4b,0x59,0xe1,0x07,0x51,0x5f,0x13,0x46,0xa2,0xdd,
-  0xee,0xda,0xc1,0x9d,0xdc,0xd7,0xb8,0x86,0x51,0x10,0x5a };
-
-//client hello with v2 header advertising tls 1.0
-static const uint8_t ssl_v2_v3_ch[] =
-{ 0x80,0x59,0x01,0x03,0x01,0x00,0x30,0x00,0x00,0x00,0x20,0x00,0x00,0x39,0x00,0x00,
-  0x38,0x00,0x00,0x35,0x00,0x00,0x16,0x00,0x00,0x13,0x00,0x00,0x0a,0x00,0x00,0x33,
-  0x00,0x00,0x32,0x00,0x00,0x2f,0x00,0x00,0x07,0x00,0x00,0x05,0x00,0x00,0x04,0x00,
-  0x00,0x15,0x00,0x00,0x12,0x00,0x00,0x09,0x00,0x00,0xff,0xda,0x86,0xfa,0xb4,0x73,
-  0x5a,0x1e,0x11,0xd1,0xdb,0x58,0x4b,0x59,0xe1,0x07,0x51,0x5f,0x13,0x46,0xa2,0xdd,
-  0xee,0xda,0xc1,0x9d,0xdc,0xd7,0xb8,0x86,0x51,0x10,0x5a };
-
-TEST_CASE("sslv2 detect", "[SslV2Curse]")
-{
-    uint32_t max_detect = static_cast<uint32_t>(SSL_State::BYTE_10_CHLNG_LEN_LSB);
-    CurseTracker tracker{ };
-
-    auto test = [&](uint32_t incr_by,const uint8_t* ch)
-        {
-            uint32_t i = 0;
-            while ( i <= max_detect )
-            {
-                if ( (i + incr_by - 1) < max_detect )
-                {
-                    CHECK(tracker.ssl.state == static_cast<SSL_State>(i));
-                    CHECK_FALSE(ssl_v2_curse(&ch[i],sizeof(uint8_t) * incr_by,&tracker));
-                }
-                else
-                {
-                    CHECK(ssl_v2_curse(&ch[i],sizeof(uint8_t) * incr_by,&tracker));
-                    CHECK(tracker.ssl.state == SSL_State::SSL_FOUND);
-                }
-
-                i += incr_by;
-            }
-            //subsequent checks must return found
-            CHECK(ssl_v2_curse(&ch[max_detect + 1],sizeof(uint8_t),&tracker));
-            CHECK(tracker.ssl.state == SSL_State::SSL_FOUND);
-        };
-
-    //sslv2 with ssl version 2
-    SECTION("1 byte v2"){ test(1,ssl_v2_ch); }
-    SECTION("2 bytes v2"){ test(2,ssl_v2_ch); }
-    SECTION("3 bytes v2"){ test(3,ssl_v2_ch); }
-    SECTION("4 bytes v2"){ test(4,ssl_v2_ch); }
-    SECTION("5 bytes v2"){ test(5,ssl_v2_ch); }
-    SECTION("6 bytes v2"){ test(6,ssl_v2_ch); }
-    SECTION("7 bytes v2"){ test(7,ssl_v2_ch); }
-    SECTION("8 bytes v2"){ test(8,ssl_v2_ch); }
-    SECTION("9 bytes v2"){ test(9,ssl_v2_ch); }
-    SECTION("10 bytes v2"){ test(10,ssl_v2_ch); }
-    SECTION("11 bytes v2"){ test(11,ssl_v2_ch);}
-
-    //sslv2 with tls version 1.0
-    SECTION("1 byte v2_v3"){ test(1,ssl_v2_v3_ch); }
-    SECTION("2 bytes v2_v3"){ test(2,ssl_v2_v3_ch); }
-    SECTION("3 bytes v2_v3"){ test(3,ssl_v2_v3_ch); }
-    SECTION("4 bytes v2_v3"){ test(4,ssl_v2_v3_ch); }
-    SECTION("5 bytes v2_v3"){ test(5,ssl_v2_v3_ch); }
-    SECTION("6 bytes v2_v3"){ test(6,ssl_v2_v3_ch); }
-    SECTION("7 bytes v2_v3"){ test(7,ssl_v2_v3_ch); }
-    SECTION("8 bytes v2_v3"){ test(8,ssl_v2_v3_ch); }
-    SECTION("9 bytes v2_v3"){ test(9,ssl_v2_v3_ch); }
-    SECTION("10 bytes v2_v3"){ test(10,ssl_v2_v3_ch); }
-    SECTION("11 bytes v2_v3"){ test(11,ssl_v2_v3_ch); }
-}
-
-TEST_CASE("sslv2 not found", "[SslV2Curse]")
-{
-    uint32_t max_detect = static_cast<uint32_t>(SSL_State::BYTE_10_CHLNG_LEN_LSB);
-    CurseTracker tracker{};
-    uint8_t bad_data[] = {0x00,0x08,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
-    auto test = [&](uint32_t fail_at_byte)
-        {
-            uint8_t ch_data[sizeof(ssl_v2_ch)];
-            memcpy(ch_data,ssl_v2_ch,sizeof(ssl_v2_ch));
-
-            ch_data[fail_at_byte] = bad_data[fail_at_byte];
-
-            for ( uint32_t i = 0; i <= fail_at_byte; i++ )
-            {
-                if ( i < fail_at_byte )
-                {
-                    CHECK(tracker.ssl.state == static_cast<SSL_State>(i));
-                    CHECK_FALSE(ssl_v2_curse(&ch_data[i],sizeof(uint8_t),&tracker));
-                }
-                else
-                {
-                    CHECK_FALSE(ssl_v2_curse(&ch_data[i],sizeof(uint8_t),&tracker));
-                    CHECK(tracker.ssl.state == SSL_State::SSL_NOT_FOUND);
-                }
-            }
-            //subsequent checks must return ssl not found
-            CHECK_FALSE(ssl_v2_curse(&ch_data[max_detect + 1],sizeof(uint8_t),&tracker));
-            CHECK(tracker.ssl.state == SSL_State::SSL_NOT_FOUND);
-        };
-
-    SECTION("byte 0"){ test(0);}
-    SECTION("byte 1"){ test(1);}
-    SECTION("byte 2"){ test(2);}
-    SECTION("byte 3"){ test(3);}
-    SECTION("byte 4"){ test(4);}
-    SECTION("byte 6"){ test(6);}
-    SECTION("byte 8"){ test(8);}
-    SECTION("byte 10"){ test(10);}
-}
-
-#endif
diff --git a/src/service_inspectors/wizard/dce_curse.cc b/src/service_inspectors/wizard/dce_curse.cc
new file mode 100644 (file)
index 0000000..6e0374a
--- /dev/null
@@ -0,0 +1,271 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2023-2023 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.
+//--------------------------------------------------------------------------
+// dce_curses.cc author Maya Dagon <mdagon@cisco.com>
+// Refactored from curses.cc
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "dce_curse.h"
+#include "curse_book.h"
+
+enum DceRpcPduType
+{
+    DCERPC_PDU_TYPE__REQUEST = 0,
+    DCERPC_PDU_TYPE__PING,
+    DCERPC_PDU_TYPE__RESPONSE,
+    DCERPC_PDU_TYPE__FAULT,
+    DCERPC_PDU_TYPE__WORKING,
+    DCERPC_PDU_TYPE__NOCALL,
+    DCERPC_PDU_TYPE__REJECT,
+    DCERPC_PDU_TYPE__ACK,
+    DCERPC_PDU_TYPE__CL_CANCEL,
+    DCERPC_PDU_TYPE__FACK,
+    DCERPC_PDU_TYPE__CANCEL_ACK,
+    DCERPC_PDU_TYPE__BIND,
+    DCERPC_PDU_TYPE__BIND_ACK,
+    DCERPC_PDU_TYPE__BIND_NACK,
+    DCERPC_PDU_TYPE__ALTER_CONTEXT,
+    DCERPC_PDU_TYPE__ALTER_CONTEXT_RESP,
+    DCERPC_PDU_TYPE__AUTH3,
+    DCERPC_PDU_TYPE__SHUTDOWN,
+    DCERPC_PDU_TYPE__CO_CANCEL,
+    DCERPC_PDU_TYPE__ORPHANED,
+    DCERPC_PDU_TYPE__MICROSOFT_PROPRIETARY_OUTLOOK2003_RPC_OVER_HTTP,
+    DCERPC_PDU_TYPE__MAX
+};
+
+/* Version 4 is for Connectionless
+ * Version 5 is for Connection oriented */
+enum DceRpcProtoMajorVers
+{
+    DCERPC_PROTO_MAJOR_VERS__4 = 4,
+    DCERPC_PROTO_MAJOR_VERS__5 = 5
+};
+
+enum DceRpcProtoMinorVers
+{
+    DCERPC_PROTO_MINOR_VERS__0 = 0,
+    DCERPC_PROTO_MINOR_VERS__1 = 1
+};
+
+bool CurseBook::dce_udp_curse(const uint8_t* data, unsigned len, CurseTracker*)
+{
+    const uint8_t dcerpc_cl_hdr_len = 80;
+    const uint8_t cl_len_offset = 74;
+
+    if ( len >= dcerpc_cl_hdr_len )
+    {
+        uint8_t version = data[0];
+        uint8_t pdu_type = data[1];
+        bool little_endian = ((data[4] & 0x10) >> 4) ? true : false;
+        uint16_t cl_len;
+
+#ifdef WORDS_BIGENDIAN
+        if ( !little_endian )
+#else
+        if ( little_endian )
+#endif  /* WORDS_BIGENDIAN */
+            cl_len = (data[cl_len_offset+1] << 8) | data[cl_len_offset];
+        else
+            cl_len = (data[cl_len_offset] << 8) | data[cl_len_offset+1];
+
+        if ( (version == DCERPC_PROTO_MAJOR_VERS__4) and
+            ((pdu_type == DCERPC_PDU_TYPE__REQUEST) or
+            (pdu_type == DCERPC_PDU_TYPE__RESPONSE) or
+            (pdu_type == DCERPC_PDU_TYPE__FAULT) or
+            (pdu_type == DCERPC_PDU_TYPE__REJECT) or
+            (pdu_type == DCERPC_PDU_TYPE__FACK)) and
+            ((cl_len != 0) and
+            (cl_len + (unsigned)dcerpc_cl_hdr_len) <= len) )
+            return true;
+    }
+
+    return false;
+}
+
+bool CurseBook::dce_tcp_curse(const uint8_t* data, unsigned len, CurseTracker* tracker)
+{
+    const uint8_t dce_rpc_co_hdr_len = 16;
+    DceTracker& dce = tracker->dce;
+
+    uint32_t n = 0;
+    while ( n < len )
+    {
+        switch ( dce.state )
+        {
+        case DCE_STATE__0: // check major version
+            if ( data[n] != DCERPC_PROTO_MAJOR_VERS__5 )
+            {
+                // go to bad state
+                dce.state = DCE_STATE__10;
+
+                return false;
+            }
+
+            dce.state = (DCE_State)((int)dce.state + 1);
+            break;
+
+        case DCE_STATE__1: // check minor version
+            if ( data[n] != DCERPC_PROTO_MINOR_VERS__0 )
+            {
+                // go to bad state
+                dce.state = DCE_STATE__10;
+
+                return false;
+            }
+
+            dce.state = (DCE_State)((int)dce.state + 1);
+            break;
+
+        case DCE_STATE__2: // pdu_type
+        {
+            uint8_t pdu_type = data[n];
+
+            if ( (pdu_type != DCERPC_PDU_TYPE__BIND) and
+                (pdu_type != DCERPC_PDU_TYPE__BIND_ACK) )
+            {
+                // go to bad state
+                dce.state = DCE_STATE__10;
+
+                return false;
+            }
+
+            dce.state = (DCE_State)((int)dce.state + 1);
+            break;
+        }
+
+        case DCE_STATE__4: //little endian
+            dce.helper = (data[n] & 0x10) << 20;
+            dce.state = (DCE_State)((int)dce.state + 1);
+            break;
+        case DCE_STATE__8:
+            dce.helper |= data[n];
+            dce.state = (DCE_State)((int)dce.state + 1);
+            break;
+        case DCE_STATE__9:
+#ifdef WORDS_BIGENDIAN
+            if ( !(dce.helper >> 24) )
+#else
+            if ( dce.helper >> 24 )
+#endif  /* WORDS_BIGENDIAN */
+                dce.helper = (data[n] << 8) | (dce.helper & 0XFF);
+            else
+            {
+                dce.helper <<=8;
+                dce.helper |= data[n];
+            }
+
+            if ( dce.helper >= dce_rpc_co_hdr_len )
+                return true;
+
+            dce.state = DCE_STATE__10;
+            break;
+
+        case DCE_STATE__10:
+            // no match
+            return false;
+        default:
+            dce.state = (DCE_State)((int)dce.state + 1);
+            break;
+        }
+
+        n++;
+    }
+
+    return false;
+}
+
+bool CurseBook::dce_smb_curse(const uint8_t* data, unsigned len, CurseTracker* tracker)
+{
+    const uint32_t dce_smb_id = 0xff534d42;  /* \xffSMB */
+    const uint32_t dce_smb2_id = 0xfe534d42;  /* \xfeSMB */
+    const uint8_t session_request = 0x81, session_response = 0x82, session_message = 0x00;
+    DceTracker& dce = tracker->dce;
+
+    uint32_t n = 0;
+    while ( n < len )
+    {
+        switch ( dce.state )
+        {
+        case DCE_STATE__0:
+            if ( data[n] == session_message )
+            {
+                dce.state = (DCE_State)((int)dce.state + 2);
+                break;
+            }
+
+            if ( data[n] == session_request or data[n] == session_response )
+            {
+                dce.state = (DCE_State)((int)dce.state + 1);
+
+                return false;
+            }
+
+            dce.state = DCE_STATE__9;
+
+            return false;
+
+        case DCE_STATE__1:
+            if ( data[n] == session_message )
+            {
+                dce.state = (DCE_State)((int)dce.state + 1);
+                break;
+            }
+
+            dce.state = DCE_STATE__9;
+
+            return false;
+
+        case DCE_STATE__5:
+            dce.helper = data[n];
+            dce.state = (DCE_State)((int)dce.state + 1);
+            break;
+
+        case DCE_STATE__6:
+        case DCE_STATE__7:
+            dce.helper <<= 8;
+            dce.helper |= data[n];
+            dce.state = (DCE_State)((int)dce.state + 1);
+            break;
+
+        case DCE_STATE__8:
+            dce.helper <<= 8;
+            dce.helper |= data[n];
+
+            if ( (dce.helper == dce_smb_id) or (dce.helper == dce_smb2_id) )
+                return true;
+
+            dce.state = (DCE_State)((int)dce.state + 1);
+            break;
+
+        case DCE_STATE__9:
+            // no match
+            return false;
+
+        default:
+            dce.state = (DCE_State)((int)dce.state + 1);
+            break;
+        }
+
+        n++;
+    }
+
+    return false;
+}
diff --git a/src/service_inspectors/wizard/dce_curse.h b/src/service_inspectors/wizard/dce_curse.h
new file mode 100644 (file)
index 0000000..a3ee3a9
--- /dev/null
@@ -0,0 +1,50 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2023-2023 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.
+//--------------------------------------------------------------------------
+// dce_curse.h author Maya Dagon <mdagon@cisco.com>
+// Refactored from curses.h
+
+#ifndef DCE_CURSE_H
+#define DCE_CURSE_H
+
+// DCE curse helps determine if the traffic being processed is DCERPC
+
+#include <cstdint>
+
+enum DCE_State
+{
+    DCE_STATE__0 = 0,
+    DCE_STATE__1,
+    DCE_STATE__2,
+    DCE_STATE__3,
+    DCE_STATE__4,
+    DCE_STATE__5,
+    DCE_STATE__6,
+    DCE_STATE__7,
+    DCE_STATE__8,
+    DCE_STATE__9,
+    DCE_STATE__10
+};
+
+class DceTracker
+{
+public:  
+    DCE_State state = DCE_State::DCE_STATE__0;
+    uint32_t helper;
+};
+
+#endif
diff --git a/src/service_inspectors/wizard/mms_curse.cc b/src/service_inspectors/wizard/mms_curse.cc
new file mode 100644 (file)
index 0000000..7bbaeb6
--- /dev/null
@@ -0,0 +1,247 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2023-2023 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.
+//--------------------------------------------------------------------------
+// mms_curses.cc author Jared Rittle <jared.rittle@cisco.com>
+// Moved from curses.cc
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "mms_curse.h"
+#include "curse_book.h"
+
+#include <assert.h>
+
+bool CurseBook::mms_curse(const uint8_t* data, unsigned len, CurseTracker* tracker)
+{
+    // peg the tracker to MMS
+    MmsTracker& mms = tracker->mms;
+
+    // if the state is set to MMS_STATE__SEARCH it means we most likely
+    // have a split pipelined message coming through and will need to
+    // reset the state
+    if ( mms.state == MMS_STATE__SEARCH )
+    {
+        mms.state = mms.last_state;
+    }
+
+    // define all known MMS tags to check for
+    enum
+    {
+        MMS_CONFIRMED_REQUEST_TAG    = 0xA0,
+        MMS_CONFIRMED_RESPONSE_TAG   = 0xA1,
+        MMS_CONFIRMED_ERROR_TAG      = 0xA2,
+        MMS_UNCONFIRMED_TAG          = 0xA3,
+        MMS_REJECT_TAG               = 0xA4,
+        MMS_CANCEL_REQUEST_TAG       = 0x85,
+        MMS_CANCEL_RESPONSE_TAG      = 0x86,
+        MMS_CANCEL_ERROR_TAG         = 0xA7,
+        MMS_INITIATE_REQUEST_TAG     = 0xA8,
+        MMS_INITIATE_RESPONSE_TAG    = 0xA9,
+        MMS_INITIATE_ERROR_TAG       = 0xAA,
+        MMS_CONCLUDE_REQUEST_TAG     = 0x8B,
+        MMS_CONCLUDE_RESPONSE_TAG    = 0x8C,
+        MMS_CONCLUDE_ERROR_TAG       = 0xAD,
+    };
+
+    uint32_t idx = 0;
+    while ( idx < len )
+    {
+        switch ( mms.state )
+        {
+            case MMS_STATE__TPKT_VER:
+            {
+                mms.state = MMS_STATE__TPKT_RES;
+                break;
+            }
+
+            case MMS_STATE__TPKT_RES:
+            {
+                mms.state = MMS_STATE__TPKT_LEN1;
+                break;
+            }
+
+            case MMS_STATE__TPKT_LEN1:
+            {
+                mms.state = MMS_STATE__TPKT_LEN2;
+                break;
+            }
+
+            case MMS_STATE__TPKT_LEN2:
+            {
+                mms.state = MMS_STATE__COTP_LEN;
+                break;
+            }
+
+            case MMS_STATE__COTP_LEN:
+            {
+                mms.state = MMS_STATE__COTP_PDU;
+                break;
+            }
+
+            case MMS_STATE__COTP_PDU:
+            {
+                // 7 6 5 4 3 2 1 0
+                // ---------------
+                // . . . . x x x x   Destination Reference
+                // x x x x . . . .   PDU Type
+                const uint32_t MMS_COTP_PDU_DT_DATA = 0x0F;
+
+                if ( data[idx] >> 0x04 != MMS_COTP_PDU_DT_DATA )
+                {
+                    mms.state = MMS_STATE__NOT_FOUND;
+                    break;
+                }
+
+                mms.state = MMS_STATE__COTP_TPDU_NUM;
+                break;
+            }
+
+            case MMS_STATE__COTP_TPDU_NUM:
+            {
+                mms.state = MMS_STATE__OSI_SESSION_SPDU;
+                break;
+            }
+
+            case MMS_STATE__OSI_SESSION_SPDU:
+            {
+                // define all known OSI Session layer SPDU tags to check
+                enum
+                {
+                    MMS_OSI_SESSION_SPDU_GT_DT = 0x01,
+                    MMS_OSI_SESSION_SPDU_CN = 0x0D,
+                    MMS_OSI_SESSION_SPDU_AC = 0x0E,
+                };
+
+                switch ( data[idx] )
+                {
+                    // check for a known MMS message tag in the event Session/Pres/ACSE aren't used
+                    case MMS_CONFIRMED_REQUEST_TAG:    // fallthrough intentional
+                    case MMS_CONFIRMED_RESPONSE_TAG:   // fallthrough intentional
+                    case MMS_CONFIRMED_ERROR_TAG:      // fallthrough intentional
+                    case MMS_UNCONFIRMED_TAG:          // fallthrough intentional
+                    case MMS_REJECT_TAG:               // fallthrough intentional
+                    case MMS_CANCEL_REQUEST_TAG:       // fallthrough intentional
+                    case MMS_CANCEL_RESPONSE_TAG:      // fallthrough intentional
+                    case MMS_CANCEL_ERROR_TAG:         // fallthrough intentional
+                    case MMS_INITIATE_REQUEST_TAG:     // fallthrough intentional
+                    case MMS_INITIATE_RESPONSE_TAG:    // fallthrough intentional
+                    case MMS_INITIATE_ERROR_TAG:       // fallthrough intentional
+                    case MMS_CONCLUDE_REQUEST_TAG:     // fallthrough intentional
+                    case MMS_CONCLUDE_RESPONSE_TAG:    // fallthrough intentional
+                    case MMS_CONCLUDE_ERROR_TAG:
+                    {
+                        // if an MMS tag exists in the remaining data,
+                        // hand off to the MMS service inspector
+                        mms.state = MMS_STATE__FOUND;
+                        break;
+                    }
+
+                    // if mms isn't found, search for an OSI Session layer
+                    case MMS_OSI_SESSION_SPDU_GT_DT: // fallthrough intentional
+                    case MMS_OSI_SESSION_SPDU_CN:    // fallthrough intentional
+                    case MMS_OSI_SESSION_SPDU_AC:
+                    {
+                        mms.state = MMS_STATE__MMS;
+                        break;
+                    }
+
+                    // if neither are found, it is most likely not MMS
+                    default:
+                    {
+                        mms.state = MMS_STATE__NOT_FOUND;
+                    }
+                }
+
+                break;
+            }
+
+            case MMS_STATE__MMS:
+            {
+                // loop through the remaining bytes in the buffer checking for known MMS tags
+                for ( uint32_t i=idx; i < len; i++ )
+                {
+                    // for each remaining byte check to see if it is in the known tag map
+                    switch ( data[i] )
+                    {
+                        case MMS_CONFIRMED_REQUEST_TAG:    // fallthrough intentional
+                        case MMS_CONFIRMED_RESPONSE_TAG:   // fallthrough intentional
+                        case MMS_CONFIRMED_ERROR_TAG:      // fallthrough intentional
+                        case MMS_UNCONFIRMED_TAG:          // fallthrough intentional
+                        case MMS_REJECT_TAG:               // fallthrough intentional
+                        case MMS_CANCEL_REQUEST_TAG:       // fallthrough intentional
+                        case MMS_CANCEL_RESPONSE_TAG:      // fallthrough intentional
+                        case MMS_CANCEL_ERROR_TAG:         // fallthrough intentional
+                        case MMS_INITIATE_REQUEST_TAG:     // fallthrough intentional
+                        case MMS_INITIATE_RESPONSE_TAG:    // fallthrough intentional
+                        case MMS_INITIATE_ERROR_TAG:       // fallthrough intentional
+                        case MMS_CONCLUDE_REQUEST_TAG:     // fallthrough intentional
+                        case MMS_CONCLUDE_RESPONSE_TAG:    // fallthrough intentional
+                        case MMS_CONCLUDE_ERROR_TAG:
+                        {
+                            // if an MMS tag exists in the remaining data,
+                            // hand off to the MMS service inspector
+                            mms.state = MMS_STATE__FOUND;
+                            break;
+                        }
+                        // no default here as it we don't know when we would hit
+                        // the first MMS tag without doing full parsing
+                    }
+
+                    // exit the loop when a state has been determined
+                    if ( mms.state == MMS_STATE__NOT_FOUND
+                        or mms.state == MMS_STATE__SEARCH
+                        or mms.state == MMS_STATE__FOUND )
+                    {
+                        break;
+                    }
+                }
+
+                break;
+            }
+
+            case MMS_STATE__FOUND:
+            {
+                mms.state = MMS_STATE__TPKT_VER;
+
+                return true;
+            }
+
+            case MMS_STATE__NOT_FOUND:
+            {
+                mms.state = MMS_STATE__TPKT_VER;
+
+                return false;
+            }
+
+            default:
+            {
+                mms.state = MMS_STATE__NOT_FOUND;
+                assert(false);
+                break;
+            }
+        }
+
+        idx++;
+    }
+
+    mms.last_state = mms.state;
+    mms.state = MMS_STATE__SEARCH;
+
+    return false;
+}
index 61f56c210bb0752f5189acd22e9c331c995de2fe..09ff7d1021efef74168d4c9475d6f56766b2f847 100644 (file)
@@ -24,8 +24,6 @@
 // conforms to the Manufacturing Message Specification (MMS) traffic defined
 // within the IEC-61850 family of protocols
 
-#include "curses.h"
-
 enum MMS_State
 {
     MMS_STATE__TPKT_VER = 0,
@@ -42,5 +40,12 @@ enum MMS_State
     MMS_STATE__NOT_FOUND,
 };
 
+class MmsTracker
+{
+public:  
+    MMS_State state = MMS_State::MMS_STATE__TPKT_VER;
+    MMS_State last_state = MMS_State::MMS_STATE__TPKT_VER;
+};
+
 #endif
 
diff --git a/src/service_inspectors/wizard/s7commplus_curse.cc b/src/service_inspectors/wizard/s7commplus_curse.cc
new file mode 100644 (file)
index 0000000..88ef696
--- /dev/null
@@ -0,0 +1,302 @@
+// Copyright (C) 2023-2023 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.
+//--------------------------------------------------------------------------
+// s7commplus_curse.cc author Jared Rittle <jared.rittle@cisco.com>
+// Moved from curses.cc
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+
+#include "s7commplus_curse.h"
+#include "curse_book.h"
+
+enum S7commplus_Protocol_Identifier
+{
+    S7COMMPLUS_PROTOCOL_IDENTIFIER__S7COMM     = 0x32,
+    S7COMMPLUS_PROTOCOL_IDENTIFIER__S7COMMPLUS = 0x72,
+};
+
+enum S7commplus_Pdu_Type
+{
+    S7COMMPLUS_PDU_TYPE__CONNECT           = 0x01,
+    S7COMMPLUS_PDU_TYPE__DATA              = 0x02,
+    S7COMMPLUS_PDU_TYPE__DATA2             = 0x03,
+    S7COMMPLUS_PDU_TYPE__KEEPALIVE         = 0xFF,
+};
+
+enum S7commplus_Opcode
+{
+    S7COMMPLUS_OPCODE__REQ                 = 0x31,
+    S7COMMPLUS_OPCODE__RES                 = 0x32,
+    S7COMMPLUS_OPCODE__NOTIFICATION        = 0x33,
+    S7COMMPLUS_OPCODE__RES2                = 0x02,
+};
+
+enum S7commplus_Function
+{
+    S7COMMPLUS_FUNCTION__EXPLORE           = 0x04BB,
+    S7COMMPLUS_FUNCTION__CREATEOBJECT      = 0x04CA,
+    S7COMMPLUS_FUNCTION__DELETEOBJECT      = 0x04D4,
+    S7COMMPLUS_FUNCTION__SETVARIABLE       = 0x04F2,
+    S7COMMPLUS_FUNCTION__GETLINK           = 0x0524,
+    S7COMMPLUS_FUNCTION__SETMULTIVAR       = 0x0542,
+    S7COMMPLUS_FUNCTION__GETMULTIVAR       = 0x054C,
+    S7COMMPLUS_FUNCTION__BEGINSEQUENCE     = 0x0556,
+    S7COMMPLUS_FUNCTION__ENDSEQUENCE       = 0x0560,
+    S7COMMPLUS_FUNCTION__INVOKE            = 0x056B,
+    S7COMMPLUS_FUNCTION__GETVARSUBSTR      = 0x0586,
+};
+
+bool CurseBook::s7commplus_curse(const uint8_t* data, unsigned len, CurseTracker* tracker)
+{
+    // peg the tracker to s7commplus
+    S7commplusTracker& s7commplus = tracker->s7commplus;
+
+    // if the state is set to S7COMMPLUS_STATE__SEARCH it means we most likely
+    // have a split pipelined message coming through and will need to
+    // reset the state
+    if ( s7commplus.state == S7COMMPLUS_STATE__SEARCH )
+    {
+        s7commplus.state = s7commplus.last_state;
+    }
+
+    uint32_t idx = 0;
+    while ( idx < len )
+    {
+        switch ( s7commplus.state )
+        {
+            case S7COMMPLUS_STATE__TPKT_VER:
+            {
+                s7commplus.state = S7COMMPLUS_STATE__TPKT_RES;
+                break;
+            }
+
+            case S7COMMPLUS_STATE__TPKT_RES:
+            {
+                s7commplus.state = S7COMMPLUS_STATE__TPKT_LEN1;
+                break;
+            }
+
+            case S7COMMPLUS_STATE__TPKT_LEN1:
+            {
+                s7commplus.state = S7COMMPLUS_STATE__TPKT_LEN2;
+                break;
+            }
+
+            case S7COMMPLUS_STATE__TPKT_LEN2:
+            {
+                s7commplus.state = S7COMMPLUS_STATE__COTP_LEN;
+                break;
+            }
+
+            case S7COMMPLUS_STATE__COTP_LEN:
+            {
+                s7commplus.state = S7COMMPLUS_STATE__COTP_PDU;
+                break;
+            }
+
+            case S7COMMPLUS_STATE__COTP_PDU:
+            {
+                // 7 6 5 4 3 2 1 0
+                // ---------------
+                // . . . . x x x x   Destination Reference
+                // x x x x . . . .   PDU Type
+                const uint32_t S7COMMPLUS_COTP_PDU_DT_DATA = 0x0F;
+
+                if ( data[idx] >> 0x04 != S7COMMPLUS_COTP_PDU_DT_DATA )
+                {
+                    s7commplus.state = S7COMMPLUS_STATE__NOT_FOUND;
+                    break;
+                }
+
+                s7commplus.state = S7COMMPLUS_STATE__COTP_TPDU_NUM;
+                break;
+            }
+
+            case S7COMMPLUS_STATE__COTP_TPDU_NUM:
+            {
+                s7commplus.state = S7COMMPLUS_STATE__PROTO_ID;
+                break;
+            }
+
+            case S7COMMPLUS_STATE__PROTO_ID:
+            {
+                // there are two possible protocol identifiers - 0x32 and 0x72
+                // 0x32 indicates the original s7comm protocol
+                //   * the original protocol is not supported within the inspector
+                //     so just catching and considering it a no match for now
+                // 0x72 indicates the s7commplus protocol
+                //   * this is the protocol on which the existing inspector focuses
+                if ( data[idx] == S7COMMPLUS_PROTOCOL_IDENTIFIER__S7COMMPLUS )
+                {
+                    s7commplus.state = S7COMMPLUS_STATE__PDU_TYPE;
+                }
+                else
+                {
+                    s7commplus.state = S7COMMPLUS_STATE__NOT_FOUND;
+                }
+                break;
+            }
+
+            case S7COMMPLUS_STATE__PDU_TYPE:
+            {
+                switch ( data[idx] )
+                {
+                    case S7COMMPLUS_PDU_TYPE__CONNECT:      // fallthrough intentional
+                    case S7COMMPLUS_PDU_TYPE__DATA:         // fallthrough intentional
+                    case S7COMMPLUS_PDU_TYPE__DATA2:        // fallthrough intentional
+                    case S7COMMPLUS_PDU_TYPE__KEEPALIVE:
+                    {
+                        s7commplus.state = S7COMMPLUS_STATE__DATALENGTH_1;
+                        break;
+                    }
+
+                    default:
+                    {
+                        s7commplus.state = S7COMMPLUS_STATE__NOT_FOUND;
+                        break;
+                    }
+                }
+
+                break;
+            }
+
+            case S7COMMPLUS_STATE__DATALENGTH_1:
+            {
+                s7commplus.state = S7COMMPLUS_STATE__DATALENGTH_2;
+                break;
+            }
+
+            case S7COMMPLUS_STATE__DATALENGTH_2:
+            {
+                s7commplus.state = S7COMMPLUS_STATE__OPCODE;
+                break;
+            }
+
+            case S7COMMPLUS_STATE__OPCODE:
+            {
+                switch ( data[idx] )
+                {
+                    case S7COMMPLUS_OPCODE__REQ:               // fallthrough intentional
+                    case S7COMMPLUS_OPCODE__RES:               // fallthrough intentional
+                    case S7COMMPLUS_OPCODE__NOTIFICATION:      // fallthrough intentional
+                    case S7COMMPLUS_OPCODE__RES2:
+                    {
+                        s7commplus.state = S7COMMPLUS_STATE__RES_1;
+                        break;
+                    }
+
+                    default:
+                    {
+                        s7commplus.state = S7COMMPLUS_STATE__NOT_FOUND;
+                        break;
+                    }
+                }
+
+                break;
+            }
+
+            case S7COMMPLUS_STATE__RES_1:
+            {
+                s7commplus.state = S7COMMPLUS_STATE__RES_2;
+                break;
+            }
+
+            case S7COMMPLUS_STATE__RES_2:
+            {
+                s7commplus.state = S7COMMPLUS_STATE__FUNCTION_1;
+                break;
+            }
+
+            case S7COMMPLUS_STATE__FUNCTION_1:
+            {
+                // make sure the function code is zeroed out before building
+                s7commplus.func = 0;
+
+                // get the high byte of the function code
+                s7commplus.func = data[idx] << 0x08;
+
+                // move on to the low byte
+                s7commplus.state = S7COMMPLUS_STATE__FUNCTION_2;
+                break;
+            }
+
+            case S7COMMPLUS_STATE__FUNCTION_2:
+            {
+                // get the low byte of the function code
+                s7commplus.func |= data[idx];
+
+                switch ( s7commplus.func )
+                {
+                    case S7COMMPLUS_FUNCTION__EXPLORE:               // fallthrough intentional
+                    case S7COMMPLUS_FUNCTION__CREATEOBJECT:          // fallthrough intentional
+                    case S7COMMPLUS_FUNCTION__DELETEOBJECT:          // fallthrough intentional
+                    case S7COMMPLUS_FUNCTION__SETVARIABLE:           // fallthrough intentional
+                    case S7COMMPLUS_FUNCTION__GETLINK:               // fallthrough intentional
+                    case S7COMMPLUS_FUNCTION__SETMULTIVAR:           // fallthrough intentional
+                    case S7COMMPLUS_FUNCTION__GETMULTIVAR:           // fallthrough intentional
+                    case S7COMMPLUS_FUNCTION__BEGINSEQUENCE:         // fallthrough intentional
+                    case S7COMMPLUS_FUNCTION__ENDSEQUENCE:           // fallthrough intentional
+                    case S7COMMPLUS_FUNCTION__INVOKE:                // fallthrough intentional
+                    case S7COMMPLUS_FUNCTION__GETVARSUBSTR:
+                    {
+                        s7commplus.state = S7COMMPLUS_STATE__FOUND;
+                        break;
+                    }
+
+                    default:
+                    {
+                        s7commplus.state = S7COMMPLUS_STATE__NOT_FOUND;
+                        break;
+                    }
+                }
+
+                break;
+            }
+
+            case S7COMMPLUS_STATE__FOUND:
+            {
+                s7commplus.state = S7COMMPLUS_STATE__TPKT_VER;
+
+                return true;
+            }
+
+            case S7COMMPLUS_STATE__NOT_FOUND:
+            {
+                s7commplus.state = S7COMMPLUS_STATE__TPKT_VER;
+
+                return false;
+            }
+
+            default:
+            {
+                s7commplus.state = S7COMMPLUS_STATE__NOT_FOUND;
+                assert(false);
+                break;
+            }
+        }
+
+        idx++;
+    }
+
+    s7commplus.last_state = s7commplus.state;
+    s7commplus.state = S7COMMPLUS_STATE__SEARCH;
+
+    return false;
+}
index f9863d3d2286e1a8be9a1cb2ee20a5e09447352e..ed44490eb6dee76e2eb096b12dbe4e3a48637b4a 100644 (file)
 // s7commplus_curse provides the ability to determine if the traffic being processed
 // conforms to the S7CommPlus protocol used in select Siemens devices
 
-#include "curses.h"
-
-enum S7commplus_Protocol_Identifier
-{
-    S7COMMPLUS_PROTOCOL_IDENTIFIER__S7COMM     = 0x32,
-    S7COMMPLUS_PROTOCOL_IDENTIFIER__S7COMMPLUS = 0x72,
-};
-
-
-enum S7commplus_Pdu_Type
-{
-    S7COMMPLUS_PDU_TYPE__CONNECT           = 0x01,
-    S7COMMPLUS_PDU_TYPE__DATA              = 0x02,
-    S7COMMPLUS_PDU_TYPE__DATA2             = 0x03,
-    S7COMMPLUS_PDU_TYPE__KEEPALIVE         = 0xFF,
-};
-
-
-enum S7commplus_Opcode
-{
-    S7COMMPLUS_OPCODE__REQ                 = 0x31,
-    S7COMMPLUS_OPCODE__RES                 = 0x32,
-    S7COMMPLUS_OPCODE__NOTIFICATION        = 0x33,
-    S7COMMPLUS_OPCODE__RES2                = 0x02,
-};
-
-
-enum S7commplus_Function
-{
-    S7COMMPLUS_FUNCTION__EXPLORE           = 0x04BB,
-    S7COMMPLUS_FUNCTION__CREATEOBJECT      = 0x04CA,
-    S7COMMPLUS_FUNCTION__DELETEOBJECT      = 0x04D4,
-    S7COMMPLUS_FUNCTION__SETVARIABLE       = 0x04F2,
-    S7COMMPLUS_FUNCTION__GETLINK           = 0x0524,
-    S7COMMPLUS_FUNCTION__SETMULTIVAR       = 0x0542,
-    S7COMMPLUS_FUNCTION__GETMULTIVAR       = 0x054C,
-    S7COMMPLUS_FUNCTION__BEGINSEQUENCE     = 0x0556,
-    S7COMMPLUS_FUNCTION__ENDSEQUENCE       = 0x0560,
-    S7COMMPLUS_FUNCTION__INVOKE            = 0x056B,
-    S7COMMPLUS_FUNCTION__GETVARSUBSTR      = 0x0586,
-};
-
+#include <cstdint>
 
 enum S7commplus_State
 {
@@ -89,5 +48,13 @@ enum S7commplus_State
     S7COMMPLUS_STATE__NOT_FOUND,
 };
 
+class S7commplusTracker
+{
+public:  
+    S7commplus_State state = S7commplus_State::S7COMMPLUS_STATE__TPKT_VER;
+    S7commplus_State last_state = S7commplus_State::S7COMMPLUS_STATE__TPKT_VER;
+    uint16_t func = 0;
+};
+
 #endif
 
diff --git a/src/service_inspectors/wizard/ssl_curse.cc b/src/service_inspectors/wizard/ssl_curse.cc
new file mode 100644 (file)
index 0000000..7c96570
--- /dev/null
@@ -0,0 +1,299 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2023-2023 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.
+//--------------------------------------------------------------------------
+// ssl_curse.cc author Maya Dagon <mdagon@cisco.com>
+// Refactored from curses.cc
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <cstdint>
+
+#include "ssl_curse.h"
+#include "curse_book.h"
+
+namespace SSL_Const
+{
+static constexpr uint8_t hdr_len = 9;
+static constexpr uint8_t sslv2_msb_set = 0x80;
+static constexpr uint8_t client_hello = 0x01;
+static constexpr uint8_t sslv3_major_ver = 0x03;
+static constexpr uint8_t sslv3_max_minor_ver = 0x03;
+}
+
+bool CurseBook::ssl_v2_curse(const uint8_t* data, unsigned len, CurseTracker* tracker)
+{
+    SslTracker& ssl = tracker->ssl;
+
+    if ( ssl.state == SSL_STATE__SSL_NOT_FOUND )
+        return false;
+    else if ( ssl.state == SSL_STATE__SSL_FOUND )
+        return true;
+
+    for ( unsigned i = 0; i < len; ++i )
+    {
+        uint8_t val = data[i];
+
+        switch ( ssl.state )
+        {
+        case SSL_STATE__BYTE_0_LEN_MSB:
+            if ( (val & SSL_Const::sslv2_msb_set) == 0 )
+            {
+                ssl.state = SSL_STATE__SSL_NOT_FOUND;
+
+                return false;
+            }
+
+            ssl.total_len = (val & (~SSL_Const::sslv2_msb_set)) << 8;
+            ssl.state = SSL_STATE__BYTE_1_LEN_LSB;
+            break;
+
+        case SSL_STATE__BYTE_1_LEN_LSB:
+            ssl.total_len |= val;
+            if ( ssl.total_len < SSL_Const::hdr_len )
+            {
+                ssl.state = SSL_STATE__SSL_NOT_FOUND;
+
+                return false;
+            }
+
+            ssl.total_len -= SSL_Const::hdr_len;
+            ssl.state = SSL_STATE__BYTE_2_CLIENT_HELLO;
+            break;
+
+        case SSL_STATE__BYTE_2_CLIENT_HELLO:
+            if ( val != SSL_Const::client_hello )
+            {
+                ssl.state = SSL_STATE__SSL_NOT_FOUND;
+
+                return false;
+            }
+
+            ssl.state = SSL_STATE__BYTE_3_MAX_MINOR_VER;
+            break;
+
+        case SSL_STATE__BYTE_3_MAX_MINOR_VER:
+            if ( val > SSL_Const::sslv3_max_minor_ver )
+            {
+                ssl.state = SSL_STATE__SSL_NOT_FOUND;
+
+                return false;
+            }
+
+            ssl.state = SSL_STATE__BYTE_4_V3_MAJOR;
+            break;
+
+        case SSL_STATE__BYTE_4_V3_MAJOR:
+            if ( val > SSL_Const::sslv3_major_ver )
+            {
+                ssl.state = SSL_STATE__SSL_NOT_FOUND;
+
+                return false;
+            }
+
+            ssl.state = SSL_STATE__BYTE_5_SPECS_LEN_MSB;
+            break;
+
+        case SSL_STATE__BYTE_5_SPECS_LEN_MSB:
+            ssl.specs_len = val << 8;
+            ssl.state = SSL_STATE__BYTE_6_SPECS_LEN_LSB;
+            break;
+
+        case SSL_STATE__BYTE_6_SPECS_LEN_LSB:
+            ssl.specs_len |= val;
+
+            if ( ssl.total_len < ssl.specs_len )
+            {
+                ssl.state = SSL_STATE__SSL_NOT_FOUND;
+
+                return false;
+            }
+
+            ssl.total_len -= ssl.specs_len;
+            ssl.state = SSL_STATE__BYTE_7_SSNID_LEN_MSB;
+            break;
+
+        case SSL_STATE__BYTE_7_SSNID_LEN_MSB:
+            ssl.ssnid_len = val << 8;
+            ssl.state = SSL_STATE__BYTE_8_SSNID_LEN_LSB;
+            break;
+
+        case SSL_STATE__BYTE_8_SSNID_LEN_LSB:
+            ssl.ssnid_len |= val;
+
+            if ( ssl.total_len < ssl.ssnid_len )
+            {
+                ssl.state = SSL_STATE__SSL_NOT_FOUND;
+
+                return false;
+            }
+
+            ssl.total_len -= ssl.ssnid_len;
+            ssl.state = SSL_STATE__BYTE_9_CHLNG_LEN_MSB;
+            break;
+
+        case SSL_STATE__BYTE_9_CHLNG_LEN_MSB:
+            ssl.chlng_len = val << 8;
+            ssl.state = SSL_STATE__BYTE_10_CHLNG_LEN_LSB;
+            break;
+
+        case SSL_STATE__BYTE_10_CHLNG_LEN_LSB:
+            ssl.chlng_len |= val;
+
+            if ( ssl.total_len < ssl.chlng_len )
+            {
+                ssl.state = SSL_STATE__SSL_NOT_FOUND;
+
+                return false;
+            }
+
+            ssl.state = SSL_STATE__SSL_FOUND;
+
+            return true;
+
+        default:
+            return false;
+        }
+    }
+
+    return false;
+}
+
+#ifdef CATCH_TEST_BUILD
+
+#include "catch/catch.hpp"
+#include <cstring>
+
+// client hello with v2 header advertising sslv2
+static const uint8_t ssl_v2_ch[] =
+{
+    0x80,0x59,0x01,0x00,0x02,0x00,0x30,0x00,0x00,0x00,0x20,0x00,0x00,0x39,0x00,0x00,
+    0x38,0x00,0x00,0x35,0x00,0x00,0x16,0x00,0x00,0x13,0x00,0x00,0x0a,0x00,0x00,0x33,
+    0x00,0x00,0x32,0x00,0x00,0x2f,0x00,0x00,0x07,0x00,0x00,0x05,0x00,0x00,0x04,0x00,
+    0x00,0x15,0x00,0x00,0x12,0x00,0x00,0x09,0x00,0x00,0xff,0xda,0x86,0xfa,0xb4,0x73,
+    0x5a,0x1e,0x11,0xd1,0xdb,0x58,0x4b,0x59,0xe1,0x07,0x51,0x5f,0x13,0x46,0xa2,0xdd,
+    0xee,0xda,0xc1,0x9d,0xdc,0xd7,0xb8,0x86,0x51,0x10,0x5a
+};
+
+// client hello with v2 header advertising tls 1.0
+static const uint8_t ssl_v2_v3_ch[] =
+{
+    0x80,0x59,0x01,0x03,0x01,0x00,0x30,0x00,0x00,0x00,0x20,0x00,0x00,0x39,0x00,0x00,
+    0x38,0x00,0x00,0x35,0x00,0x00,0x16,0x00,0x00,0x13,0x00,0x00,0x0a,0x00,0x00,0x33,
+    0x00,0x00,0x32,0x00,0x00,0x2f,0x00,0x00,0x07,0x00,0x00,0x05,0x00,0x00,0x04,0x00,
+    0x00,0x15,0x00,0x00,0x12,0x00,0x00,0x09,0x00,0x00,0xff,0xda,0x86,0xfa,0xb4,0x73,
+    0x5a,0x1e,0x11,0xd1,0xdb,0x58,0x4b,0x59,0xe1,0x07,0x51,0x5f,0x13,0x46,0xa2,0xdd,
+    0xee,0xda,0xc1,0x9d,0xdc,0xd7,0xb8,0x86,0x51,0x10,0x5a
+};
+
+TEST_CASE("sslv2 detect", "[SslV2Curse]")
+{
+    uint32_t max_detect = static_cast<uint32_t>(SSL_STATE__BYTE_10_CHLNG_LEN_LSB);
+    CurseTracker tracker{ };
+
+    auto test = [&](uint32_t incr_by,const uint8_t* ch)
+        {
+            uint32_t i = 0;
+            while ( i <= max_detect )
+            {
+                if ( (i + incr_by - 1) < max_detect )
+                {
+                    CHECK(tracker.ssl.state == static_cast<SSL_State>(i));
+                    CHECK_FALSE(CurseBook::ssl_v2_curse(&ch[i],sizeof(uint8_t) * incr_by,&tracker));
+                }
+                else
+                {
+                    CHECK(CurseBook::ssl_v2_curse(&ch[i],sizeof(uint8_t) * incr_by,&tracker));
+                    CHECK(tracker.ssl.state == SSL_STATE__SSL_FOUND);
+                }
+
+                i += incr_by;
+            }
+            // subsequent checks must return found
+            CHECK(CurseBook::ssl_v2_curse(&ch[max_detect + 1],sizeof(uint8_t),&tracker));
+            CHECK(tracker.ssl.state == SSL_STATE__SSL_FOUND);
+        };
+
+    // sslv2 with ssl version 2
+    SECTION("1 byte v2"){ test(1,ssl_v2_ch); }
+    SECTION("2 bytes v2"){ test(2,ssl_v2_ch); }
+    SECTION("3 bytes v2"){ test(3,ssl_v2_ch); }
+    SECTION("4 bytes v2"){ test(4,ssl_v2_ch); }
+    SECTION("5 bytes v2"){ test(5,ssl_v2_ch); }
+    SECTION("6 bytes v2"){ test(6,ssl_v2_ch); }
+    SECTION("7 bytes v2"){ test(7,ssl_v2_ch); }
+    SECTION("8 bytes v2"){ test(8,ssl_v2_ch); }
+    SECTION("9 bytes v2"){ test(9,ssl_v2_ch); }
+    SECTION("10 bytes v2"){ test(10,ssl_v2_ch); }
+    SECTION("11 bytes v2"){ test(11,ssl_v2_ch);}
+
+    // sslv2 with tls version 1.0
+    SECTION("1 byte v2_v3"){ test(1,ssl_v2_v3_ch); }
+    SECTION("2 bytes v2_v3"){ test(2,ssl_v2_v3_ch); }
+    SECTION("3 bytes v2_v3"){ test(3,ssl_v2_v3_ch); }
+    SECTION("4 bytes v2_v3"){ test(4,ssl_v2_v3_ch); }
+    SECTION("5 bytes v2_v3"){ test(5,ssl_v2_v3_ch); }
+    SECTION("6 bytes v2_v3"){ test(6,ssl_v2_v3_ch); }
+    SECTION("7 bytes v2_v3"){ test(7,ssl_v2_v3_ch); }
+    SECTION("8 bytes v2_v3"){ test(8,ssl_v2_v3_ch); }
+    SECTION("9 bytes v2_v3"){ test(9,ssl_v2_v3_ch); }
+    SECTION("10 bytes v2_v3"){ test(10,ssl_v2_v3_ch); }
+    SECTION("11 bytes v2_v3"){ test(11,ssl_v2_v3_ch); }
+}
+
+TEST_CASE("sslv2 not found", "[SslV2Curse]")
+{
+    uint32_t max_detect = static_cast<uint32_t>(SSL_STATE__BYTE_10_CHLNG_LEN_LSB);
+    CurseTracker tracker{};
+    uint8_t bad_data[] = {0x00,0x08,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
+    auto test = [&](uint32_t fail_at_byte)
+        {
+            uint8_t ch_data[sizeof(ssl_v2_ch)];
+            memcpy(ch_data,ssl_v2_ch,sizeof(ssl_v2_ch));
+
+            ch_data[fail_at_byte] = bad_data[fail_at_byte];
+
+            for ( uint32_t i = 0; i <= fail_at_byte; i++ )
+            {
+                if ( i < fail_at_byte )
+                {
+                    CHECK(tracker.ssl.state == static_cast<SSL_State>(i));
+                    CHECK_FALSE(CurseBook::ssl_v2_curse(&ch_data[i],sizeof(uint8_t),&tracker));
+                }
+                else
+                {
+                    CHECK_FALSE(CurseBook::ssl_v2_curse(&ch_data[i],sizeof(uint8_t),&tracker));
+                    CHECK(tracker.ssl.state == SSL_STATE__SSL_NOT_FOUND);
+                }
+            }
+            // subsequent checks must return ssl not found
+            CHECK_FALSE(CurseBook::ssl_v2_curse(&ch_data[max_detect + 1],sizeof(uint8_t),&tracker));
+            CHECK(tracker.ssl.state == SSL_STATE__SSL_NOT_FOUND);
+        };
+
+    SECTION("byte 0"){ test(0);}
+    SECTION("byte 1"){ test(1);}
+    SECTION("byte 2"){ test(2);}
+    SECTION("byte 3"){ test(3);}
+    SECTION("byte 4"){ test(4);}
+    SECTION("byte 6"){ test(6);}
+    SECTION("byte 8"){ test(8);}
+    SECTION("byte 10"){ test(10);}
+}
+
+#endif
diff --git a/src/service_inspectors/wizard/ssl_curse.h b/src/service_inspectors/wizard/ssl_curse.h
new file mode 100644 (file)
index 0000000..cb83a5e
--- /dev/null
@@ -0,0 +1,53 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2023-2023 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.
+//--------------------------------------------------------------------------
+// ssl_curse.h author Maya Dagon <mdagon@cisco.com>
+// Refactored from curses.h
+
+#ifndef SSL_CURSE_H
+#define SSL_CURSE_H
+
+// SSL curse helps determine if the traffic being processed is SSL
+
+enum SSL_State
+{
+    SSL_STATE__BYTE_0_LEN_MSB = 0,
+    SSL_STATE__BYTE_1_LEN_LSB,
+    SSL_STATE__BYTE_2_CLIENT_HELLO,
+    SSL_STATE__BYTE_3_MAX_MINOR_VER,
+    SSL_STATE__BYTE_4_V3_MAJOR,
+    SSL_STATE__BYTE_5_SPECS_LEN_MSB,
+    SSL_STATE__BYTE_6_SPECS_LEN_LSB,
+    SSL_STATE__BYTE_7_SSNID_LEN_MSB,
+    SSL_STATE__BYTE_8_SSNID_LEN_LSB,
+    SSL_STATE__BYTE_9_CHLNG_LEN_MSB,
+    SSL_STATE__BYTE_10_CHLNG_LEN_LSB,
+    SSL_STATE__SSL_FOUND,
+    SSL_STATE__SSL_NOT_FOUND
+};
+
+class SslTracker
+{
+public:
+    SSL_State state = SSL_STATE__BYTE_0_LEN_MSB;
+    unsigned total_len;
+    unsigned ssnid_len;
+    unsigned specs_len;
+    unsigned chlng_len;
+};
+
+#endif
index 4b329ab7e709279ccea8ea042021ce9af2999f5f..25bbd8d5e56fa994b683445963de814db113a7ba 100644 (file)
@@ -27,7 +27,7 @@
 #include "log/messages.h"
 #include "trace/trace.h"
 
-#include "curses.h"
+#include "curse_book.h"
 
 using namespace snort;
 using namespace std;
index 3f173919adb44e095d901e6b80c7c5e2c789be61..7919f622cacf04c68d31d25d759e5ae4a7bad633 100644 (file)
@@ -29,7 +29,7 @@
 #include "stream/stream_splitter.h"
 #include "trace/trace_api.h"
 
-#include "curses.h"
+#include "curse_book.h"
 #include "magic.h"
 #include "wiz_module.h"