From: George Koikara (gkoikara) Date: Sat, 11 Jul 2020 16:53:59 +0000 (+0000) Subject: Merge pull request #2202 in SNORT/snort3 from ~BJANDHYA/snort3:feature/smb2 to master X-Git-Tag: 3.0.2-2~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=243b1f5924f50bc2731d9c0f2ca641ee20f5145b;p=thirdparty%2Fsnort3.git Merge pull request #2202 in SNORT/snort3 from ~BJANDHYA/snort3:feature/smb2 to master Squashed commit of the following: commit cbba5e98aa31048ffbfac913d9cde0a0e7bb7238 Author: Bhargava Jandhyala Date: Tue Jan 21 04:07:25 2020 -0500 smb: adding support for multiple smbv2 session for same tcp connection 1. introduced new structures to process multiple smbv2 sessions parallely. 2. SMB Session Data (SSD): The session data corresponds to a single TCP session and it holds all the flow related information. This differenciats the SMB version and segegates the processing. There can be multiple SMB sessions in a single TCP connection, hence this holds a list of session trackers. 3. Session Tracker : Session tracker uniqly tracks a single SMB session. This is identified by the uniq session_id and this holds the mount points named as Tree Trackers 4. smbv2 session will be stored in global LRU cache as well as in locally in SSD with session id. 5. Tree Tracker : Tree tracker accounts the mount points in a share and this holds the file trackers for individual file transfers 6. File Tracker : file tracker is responsible for identifying a file transfer and track it from start to end. This manages all the information related to file in transit. 6. Request trackers: Request trackers are needed to track the activity of various requests. There are 2 types of request being tracked by trackers named the create request trackers and read request trackers 7. Storage abstraction: A storage abstraction is used to store all of the trackers. Currently the underlying storage is unordered map, but can be modified as needed. --- diff --git a/src/service_inspectors/dce_rpc/CMakeLists.txt b/src/service_inspectors/dce_rpc/CMakeLists.txt index c94c51bab..a8b19ad04 100644 --- a/src/service_inspectors/dce_rpc/CMakeLists.txt +++ b/src/service_inspectors/dce_rpc/CMakeLists.txt @@ -6,6 +6,7 @@ set( FILE_LIST dce_common.h dce_context_data.cc dce_context_data.h + dce_db.h dce_expected_session.cc dce_expected_session.h dce_http_proxy.cc @@ -24,6 +25,10 @@ set( FILE_LIST dce_smb.h dce_smb2.cc dce_smb2.h + dce_smb2_commands.cc + dce_smb2_commands.h + dce_smb2_utils.cc + dce_smb2_utils.h dce_smb_commands.cc dce_smb_commands.h dce_smb_module.cc diff --git a/src/service_inspectors/dce_rpc/dce_common.cc b/src/service_inspectors/dce_rpc/dce_common.cc index c1a6f0b8c..c56a74b1b 100644 --- a/src/service_inspectors/dce_rpc/dce_common.cc +++ b/src/service_inspectors/dce_rpc/dce_common.cc @@ -158,8 +158,7 @@ void DCE2_Detect(DCE2_SsnData* sd) DCE2_TransType get_dce2_trans_type(const Packet* p) { - DCE2_SmbSsnData* smb_data = get_dce2_smb_session_data(p->flow); - DCE2_SsnData* sd = (smb_data != nullptr) ? &(smb_data->sd) : nullptr; + DCE2_SsnData* sd = get_dce2_session_data(p->flow); if ((sd != nullptr) && (sd->trans == DCE2_TRANS_TYPE__SMB)) { return DCE2_TRANS_TYPE__SMB; diff --git a/src/service_inspectors/dce_rpc/dce_db.h b/src/service_inspectors/dce_rpc/dce_db.h new file mode 100644 index 000000000..b3653fd9c --- /dev/null +++ b/src/service_inspectors/dce_rpc/dce_db.h @@ -0,0 +1,137 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2020 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_db.h author Neha Sharma + +// This implementation provides interface that can be extended for map, list, etc. +// Currently only map has been implemented to handle multiple smb sessions +// in single tcp connection. This database will modify/change to handle +// single smb session spread across multiple tcp connections. + +#ifndef DCE_DB_H +#define DCE_DB_H + +#include +#include +#include "dce_utils.h" + +#include "main/snort_types.h" + +// Callbacks +typedef void (* DCE2_DbDataFree)(void*); + +template +class DCE2_Db +{ +public: + + virtual void Init(const DCE2_DbDataFree func) = 0; + virtual DCE2_Ret Insert(const Key& key, Value data) = 0; + virtual Value Find(const Key& key) = 0; + virtual void Remove(const Key& key) = 0; + virtual int GetSize() = 0; + virtual std::vector< std::pair > get_all_entry() = 0; +}; + +template +class DCE2_DbMap : public DCE2_Db +{ +public: + + DCE2_DbMap() + { + data_free = nullptr; + } + + ~DCE2_DbMap() + { + auto it = Map.cbegin(); + while (it != Map.cend()) + { + if (data_free) + data_free((void*)(it->second)); + else + delete it->second; + it = Map.erase(it); + } + } + + void Init(const DCE2_DbDataFree func); + DCE2_Ret Insert(const Key& key, Value data); + Value Find(const Key& key); + void Remove(const Key& key); + int GetSize() + { + return Map.size(); + } + std::vector< std::pair > get_all_entry(); + +private: + std::unordered_map Map; + DCE2_DbDataFree data_free; +}; + +template +void DCE2_DbMap::Init(const DCE2_DbDataFree df) +{ + data_free = df; +} + +template +DCE2_Ret DCE2_DbMap::Insert(const Key& key, Value data) +{ + Map[key] = data; + return DCE2_RET__SUCCESS; +} + +template +Value DCE2_DbMap::Find(const Key& key) +{ + auto elem = Map.find(key); + if (elem != Map.end()) + return elem->second; + return nullptr; +} + +template +void DCE2_DbMap::Remove(const Key& key) +{ + auto elem = Map.find(key); + if (elem != Map.end()) + { + if (data_free) + data_free((void*)(elem->second)); + else + delete elem->second; + Map.erase(elem->first); + } +} + +template +std::vector< std::pair > +DCE2_DbMap::get_all_entry() +{ + std::vector > vec; + + for (auto& entry : Map ) + { + vec.emplace_back(entry); + } + + return vec; +} +#endif diff --git a/src/service_inspectors/dce_rpc/dce_smb.cc b/src/service_inspectors/dce_rpc/dce_smb.cc index c093e9802..bb339c881 100644 --- a/src/service_inspectors/dce_rpc/dce_smb.cc +++ b/src/service_inspectors/dce_rpc/dce_smb.cc @@ -27,15 +27,14 @@ #include "detection/detection_engine.h" #include "file_api/file_service.h" #include "protocols/packet.h" -#include "utils/util.h" #include "dce_context_data.h" #include "dce_smb_commands.h" #include "dce_smb_module.h" #include "dce_smb_paf.h" #include "dce_smb_transaction.h" -#include "dce_smb_utils.h" #include "dce_smb2.h" +#include "dce_smb2_utils.h" using namespace snort; @@ -355,38 +354,74 @@ void Dce2Smb::show(const SnortConfig*) const void Dce2Smb::eval(Packet* p) { - DCE2_SmbSsnData* dce2_smb_sess; + DCE2_SmbSsnData* dce2_smb_sess = nullptr; + DCE2_Smb2SsnData* dce2_smb2_sess = nullptr; + DCE2_SmbVersion smb_version = DCE2_SMB_VERSION_NULL; Profile profile(dce2_smb_pstat_main); assert(p->has_tcp_data()); assert(p->flow); - dce2_smb_sess = dce2_handle_smb_session(p, &config); - - if (dce2_smb_sess) + Dce2SmbFlowData *smb_flowdata = (Dce2SmbFlowData*)p->flow->get_flow_data(Dce2SmbFlowData::inspector_id); + if (smb_flowdata and smb_flowdata->dce2_smb_session_data) { - p->packet_flags |= PKT_ALLOW_MULTIPLE_DETECT; - dce2_detected = 0; - - p->endianness = new DceEndianness(); + smb_version = smb_flowdata->smb_version; + if (DCE2_SMB_VERSION_1 == smb_version) + dce2_smb_sess = (DCE2_SmbSsnData*)smb_flowdata->dce2_smb_session_data; + else + dce2_smb2_sess = (DCE2_Smb2SsnData*)smb_flowdata->dce2_smb_session_data; + } + else + { + smb_version = DCE2_Smb2Version(p); + + if (DCE2_SMB_VERSION_1 == smb_version) + { + //1st packet of flow in smb1 session, create smb1 session and flowdata + dce2_smb_sess = dce2_create_new_smb_session(p, &config); + } + else if (DCE2_SMB_VERSION_2 == smb_version) + { + //1st packet of flow in smb2 session, create smb2 session and flowdata + dce2_smb2_sess = dce2_create_new_smb2_session(p, &config); + } + else + { + //smb_version is DCE2_SMB_VERSION_NULL + //This means there is no flow data and this is not an SMB packet + //if it is a TCP packet for smb data, the flow must have been + //already identified with version. + return; + } + } - DCE2_SmbProcess(dce2_smb_sess); + // By this time we must know the smb version, have correct smb session data and created flowdata + p->packet_flags |= PKT_ALLOW_MULTIPLE_DETECT; + dce2_detected = 0; + p->endianness = new DceEndianness(); + if (DCE2_SMB_VERSION_1 == smb_version) + { + DCE2_Smb1Process(dce2_smb_sess); if (!dce2_detected) DCE2_Detect(&dce2_smb_sess->sd); - - delete p->endianness; - p->endianness = nullptr; } + else + { + DCE2_Smb2Process(dce2_smb2_sess); + if (!dce2_detected) + DCE2_Detect(&dce2_smb2_sess->sd); + } + + delete(p->endianness); + p->endianness = nullptr; } void Dce2Smb::clear(Packet* p) { - DCE2_SmbSsnData* dce2_smb_sess = get_dce2_smb_session_data(p->flow); - if ( dce2_smb_sess ) - { - DCE2_ResetRopts(&dce2_smb_sess->sd, p); - } + DCE2_SsnData* sd = get_dce2_session_data(p->flow); + if ( sd ) + DCE2_ResetRopts(sd, p); } //------------------------------------------------------------------------- @@ -411,11 +446,22 @@ static void dce2_smb_init() DceContextData::init(DCE2_TRANS_TYPE__SMB); } +static void dce2_smb_thread_int() +{ + DCE2_SmbSessionCacheInit(session_cache_size); +} + +static void dce_smb_thread_term() +{ + delete smb2_session_cache; +} + static Inspector* dce2_smb_ctor(Module* m) { Dce2SmbModule* mod = (Dce2SmbModule*)m; dce2SmbProtoConf config; mod->get_data(config); + session_cache_size = DCE2_ScSmbMemcap(&config)/1024; return new Dce2Smb(config); } @@ -444,8 +490,8 @@ const InspectApi dce2_smb_api = "netbios-ssn", dce2_smb_init, nullptr, // pterm - nullptr, // tinit - nullptr, // tterm + dce2_smb_thread_int, // tinit + dce_smb_thread_term, // tterm dce2_smb_ctor, dce2_smb_dtor, nullptr, // ssn diff --git a/src/service_inspectors/dce_rpc/dce_smb.h b/src/service_inspectors/dce_rpc/dce_smb.h index d716fd9e2..aeb0a32ed 100644 --- a/src/service_inspectors/dce_rpc/dce_smb.h +++ b/src/service_inspectors/dce_rpc/dce_smb.h @@ -22,11 +22,9 @@ #ifndef DCE_SMB_H #define DCE_SMB_H -#include "framework/counts.h" +#include "dce_co.h" #include "protocols/packet.h" #include "profiler/profiler_defs.h" - -#include "dce_co.h" #include "smb_common.h" #include "smb_message.h" @@ -180,13 +178,60 @@ struct dce2SmbStats */ PegCount smb_files_processed; /* SMB2 stats */ - PegCount smb2_create; - PegCount smb2_write; - PegCount smb2_read; - PegCount smb2_set_info; - PegCount smb2_tree_connect; - PegCount smb2_tree_disconnect; - PegCount smb2_close; + PegCount v2_setup; + PegCount v2_setup_err_resp; + PegCount v2_setup_inv_str_sz; + PegCount v2_setup_resp_hdr_err; + PegCount v2_tree_cnct; + PegCount v2_tree_cnct_err_resp; + PegCount v2_tree_cnct_ignored; + PegCount v2_tree_cnct_inv_str_sz; + PegCount v2_tree_cnct_resp_hdr_err; + PegCount v2_crt; + PegCount v2_crt_err_resp; + PegCount v2_crt_inv_file_data; + PegCount v2_crt_inv_str_sz; + PegCount v2_crt_resp_hdr_err; + PegCount v2_crt_req_hdr_err; + PegCount v2_crt_rtrkr_misng; + PegCount v2_crt_req_ipc; + PegCount v2_crt_tree_trkr_misng; + PegCount v2_wrt; + PegCount v2_wrt_err_resp; + PegCount v2_wrt_ignored; + PegCount v2_wrt_inv_str_sz; + PegCount v2_wrt_req_hdr_err; + PegCount v2_read; + PegCount v2_read_err_resp; + PegCount v2_read_ignored; + PegCount v2_read_inv_str_sz; + PegCount v2_read_rtrkr_misng; + PegCount v2_read_resp_hdr_err; + PegCount v2_read_req_hdr_err; + PegCount v2_stinf; + PegCount v2_stinf_err_resp; + PegCount v2_stinf_ignored; + PegCount v2_stinf_inv_str_sz; + PegCount v2_stinf_req_ftrkr_misng; + PegCount v2_stinf_req_hdr_err; + PegCount v2_cls; + PegCount v2_cls_err_resp; + PegCount v2_cls_ignored; + PegCount v2_cls_inv_str_sz; + PegCount v2_cls_req_ftrkr_misng; + PegCount v2_cls_req_hdr_err; + PegCount v2_tree_discn; + PegCount v2_tree_discn_ignored; + PegCount v2_tree_discn_inv_str_sz; + PegCount v2_tree_discn_req_hdr_err; + PegCount v2_logoff; + PegCount v2_logoff_inv_str_sz; + PegCount v2_hdr_err; + PegCount v2_bad_next_cmd_offset; + PegCount v2_extra_file_data_err; + PegCount v2_inv_file_ctx_err; + PegCount v2_msgs_uninspected; + PegCount v2_cmpnd_req_lt_crossed; PegCount concurrent_sessions; PegCount max_concurrent_sessions; }; @@ -250,9 +295,9 @@ struct DCE2_SmbFileChunk enum DCE2_SmbVersion { - DCE2_SMB_VERISON_NULL, - DCE2_SMB_VERISON_1, - DCE2_SMB_VERISON_2 + DCE2_SMB_VERSION_NULL, + DCE2_SMB_VERSION_1, + DCE2_SMB_VERSION_2 }; struct DCE2_SmbFileTracker @@ -307,6 +352,8 @@ struct DCE2_SmbFileTracker } file; } tracker; + DCE2_SmbPduState smb2_pdu_state; + #define fid_v1 file_key.id_smb1.file_id #define uid_v1 file_key.id_smb1.u_id #define tid_v1 file_key.id_smb1.tree_id @@ -388,7 +435,6 @@ struct DCE2_SmbRequestTracker struct DCE2_SmbSsnData { DCE2_SsnData sd; // This member must be first - DCE2_Policy policy; int dialect_index; @@ -429,8 +475,6 @@ struct DCE2_SmbSsnData // This is a reference to a file tracker so shouldn't be freed. DCE2_SmbFileTracker* fapi_ftracker; - Smb2Request* smb2_requests; - DCE2_SmbFileTracker* fb_ftracker; bool block_pdus; @@ -459,7 +503,8 @@ public: public: static unsigned inspector_id; - DCE2_SmbSsnData dce2_smb_session; + DCE2_SmbVersion smb_version; + void* dce2_smb_session_data; }; // Used for reassembled packets @@ -468,7 +513,7 @@ public: #define DCE2_MOCK_HDR_LEN__SMB_SRV \ ((unsigned)(sizeof(NbssHdr) + sizeof(SmbNtHdr) + sizeof(SmbReadAndXResp))) -DCE2_SmbSsnData* get_dce2_smb_session_data(snort::Flow*); +DCE2_SsnData* get_dce2_session_data(snort::Flow*); const char* get_smb_com_string(uint8_t); #endif diff --git a/src/service_inspectors/dce_rpc/dce_smb2.cc b/src/service_inspectors/dce_rpc/dce_smb2.cc index f2adcdbbb..525424204 100644 --- a/src/service_inspectors/dce_rpc/dce_smb2.cc +++ b/src/service_inspectors/dce_rpc/dce_smb2.cc @@ -24,783 +24,210 @@ #endif #include "dce_smb2.h" - +#include "dce_smb2_commands.h" #include "detection/detection_util.h" -#include "file_api/file_flows.h" -#include "file_api/file_service.h" #include "main/snort_debug.h" -#include "utils/util.h" - -#include "dce_smb_module.h" -#include "dce_smb_utils.h" using namespace snort; -#define UNKNOWN_FILE_SIZE ~0 -#define SMB2_MAX_OUTSTANDING_REQUESTS 128 - -// FIXIT-L port fileCache related code along with -// DCE2_Smb2Init, DCE2_Smb2Close and DCE2_Smb2UpdateStats - -static void DCE2_Smb2Inspect(DCE2_SmbSsnData* ssd, const Smb2Hdr* smb_hdr, - const uint8_t* end); - -static inline uint32_t Smb2Tid(const Smb2Hdr* hdr) -{ - return alignedNtohl(&(((const Smb2SyncHdr*)hdr)->tree_id)); -} - -static int DCE2_Smb2TidCompare(const void* a, const void* b) -{ - uint32_t x = (uint32_t)(uintptr_t)a; - uint32_t y = (uint32_t)(uintptr_t)b; - - if (x == y) - return 0; - - /* Only care about equality for finding */ - return -1; -} - -static inline DCE2_Ret DCE2_Smb2InsertTid(DCE2_SmbSsnData* ssd, const uint32_t tid, - const uint8_t share_type) -{ - DCE2_Ret ret = DCE2_RET__ERROR; - - if ( (share_type == SMB2_SHARE_TYPE_DISK)and - (ssd->max_file_depth == - -1 and DCE2_ScSmbFileDepth((dce2SmbProtoConf*)ssd->sd.config) == -1) ) - { - debug_logf(dce_smb_trace, nullptr, "Not inserting TID (%u) for DISK share type " - "as mandatory configuration max_file_depth is not present." - "This will result in non-inspection of file data.\n", tid); - return ret; - } - - if (ssd->tids == nullptr) - { - ssd->tids = DCE2_ListNew(DCE2_LIST_TYPE__SPLAYED, DCE2_Smb2TidCompare, - nullptr, nullptr, DCE2_LIST_FLAG__NO_DUPS); - - if (ssd->tids == nullptr) - { - return ret; - } - } - - if (ssd->outstanding_requests >= SMB2_MAX_OUTSTANDING_REQUESTS) - { - return ret; - } - - ret = DCE2_ListInsert(ssd->tids, (void*)(uintptr_t)tid, (void*)(uintptr_t)share_type); - if (DCE2_RET__SUCCESS == ret) - { - ssd->outstanding_requests++; - } - - return ret; -} - -static inline uint8_t DCE2_Smb2GetShareType(DCE2_SmbSsnData* ssd, const uint32_t tid) -{ - if ( DCE2_RET__SUCCESS == DCE2_ListFindKey(ssd->tids, (void*)(uintptr_t)tid) ) - return ((uint8_t)(uintptr_t)DCE2_ListFind(ssd->tids, (void*)(uintptr_t)tid)); - - return SMB2_SHARE_TYPE_NONE; -} - -static inline void DCE2_Smb2RemoveTid(DCE2_SmbSsnData* ssd, const uint32_t tid) -{ - if ( DCE2_RET__SUCCESS == DCE2_ListRemove(ssd->tids, (void*)(uintptr_t)tid) ) - ssd->outstanding_requests--; -} - -static inline void DCE2_Smb2StoreRequest(DCE2_SmbSsnData* ssd, - uint64_t message_id, uint64_t offset, uint64_t file_id) -{ - Smb2Request* request = ssd->smb2_requests; - - while (request) - { - if (request->message_id == message_id) - return; - request = request->next; - } - - if (ssd->outstanding_requests >= SMB2_MAX_OUTSTANDING_REQUESTS) - { - dce_alert(GID_DCE2, DCE2_SMB_MAX_REQS_EXCEEDED, (dce2CommonStats*)&dce2_smb_stats, - ssd->sd); - return; - } - - request = ( Smb2Request* )snort_alloc( sizeof( *request ) ); - - ssd->outstanding_requests++; - - request->message_id = message_id; - request->offset = offset; - request->file_id = file_id; - - request->next = ssd->smb2_requests; - request->previous = nullptr; - if (ssd->smb2_requests) - ssd->smb2_requests->previous = request; - ssd->smb2_requests = request; -} - -static inline Smb2Request* DCE2_Smb2GetRequest(DCE2_SmbSsnData* ssd, - uint64_t message_id) -{ - Smb2Request* request = ssd->smb2_requests; - while (request) - { - if (request->message_id == message_id) - return request; - request = request->next; - } - - return nullptr; -} - -static inline void DCE2_Smb2RemoveRequest(DCE2_SmbSsnData* ssd, - Smb2Request* request) -{ - if (request->previous) - { - request->previous->next = request->next; - } - - if (request->next) - { - request->next->previous = request->previous; - } - - if (request == ssd->smb2_requests) - { - ssd->smb2_requests = request->next; - } - - ssd->outstanding_requests--; - snort_free((void*)request); -} - -static inline void DCE2_Smb2FreeFileName(DCE2_SmbFileTracker* ftracker) -{ - if (ftracker->file_name) - { - snort_free((void*)ftracker->file_name); - ftracker->file_name = nullptr; - } - ftracker->file_name_size = 0; -} - -static inline void DCE2_Smb2ResetFileName(DCE2_SmbFileTracker* ftracker) -{ - // FIXIT-L remove snort_free once file cache is ported. - if (ftracker->file_name) - { - snort_free((void*)ftracker->file_name); - } - ftracker->file_name = nullptr; - ftracker->file_name_size = 0; -} - -static inline FileContext* get_file_context(uint64_t file_id) -{ - FileFlows* file_flows = FileFlows::get_file_flows(DetectionEngine::get_current_packet()->flow); - - if ( !file_flows ) - return nullptr; - - return file_flows->get_file_context(file_id, true); -} - -static inline void DCE2_Smb2ProcessFileData(DCE2_SmbSsnData* ssd, const uint8_t* file_data, - uint32_t data_size, FileDirection dir) -{ - int64_t file_detection_depth = DCE2_ScSmbFileDepth((dce2SmbProtoConf*)ssd->sd.config); - int64_t detection_size = 0; - - if (file_detection_depth == 0) - detection_size = data_size; - else if ( ssd->ftracker.tracker.file.file_offset < (uint64_t)file_detection_depth) - { - if ( file_detection_depth - ssd->ftracker.tracker.file.file_offset < data_size ) - detection_size = file_detection_depth - ssd->ftracker.tracker.file.file_offset; - else - detection_size = data_size; - } - - if (detection_size) - { - set_file_data(file_data, - (detection_size > UINT16_MAX) ? UINT16_MAX : (uint16_t)detection_size); - - DCE2_FileDetect(); - } - - Packet* p = DetectionEngine::get_current_packet(); - FileFlows* file_flows = FileFlows::get_file_flows(p->flow); - if ( !file_flows ) - return; - - file_flows->file_process(p, ssd->ftracker.fid_v2, file_data, data_size, - ssd->ftracker.tracker.file.file_offset, dir); -} - -/******************************************************************** - * - * Process tree connect command - * Share type is defined here - * - ********************************************************************/ -static void DCE2_Smb2TreeConnect(DCE2_SmbSsnData* ssd, const Smb2Hdr* smb_hdr, - const uint8_t* smb_data, const uint8_t* end) -{ - /* Using structure size to decide whether it is response or request*/ - uint16_t structure_size; - const Smb2TreeConnectResponseHdr* smb_tree_connect_hdr = - (const Smb2TreeConnectResponseHdr*)smb_data; - - if ((const uint8_t*)smb_tree_connect_hdr + SMB2_TREE_CONNECT_RESPONSE_STRUC_SIZE > end) - return; - - structure_size = alignedNtohs(&(smb_tree_connect_hdr->structure_size)); - - if (structure_size == SMB2_TREE_CONNECT_RESPONSE_STRUC_SIZE) - { - DCE2_Smb2InsertTid(ssd, Smb2Tid(smb_hdr), smb_tree_connect_hdr->share_type); - } -} - -/******************************************************************** - * - * Process tree connect command - * Share type is defined here - * - ********************************************************************/ -static void DCE2_Smb2TreeDisconnect(DCE2_SmbSsnData* ssd, const Smb2Hdr* smb_hdr, - const uint8_t* smb_data, const uint8_t* end) +static inline bool DCE2_Smb2FindSidTid(DCE2_Smb2SsnData* ssd, const uint64_t sid, + const uint32_t tid, DCE2_Smb2SessionTracker** str, DCE2_Smb2TreeTracker** ttr) { - /* Using structure size to decide whether it is response or request*/ - uint16_t structure_size; - const Smb2TreeDisConnectHdr* smb_tree_disconnect_hdr = (const Smb2TreeDisConnectHdr*)smb_data; - - if ((const uint8_t*)smb_tree_disconnect_hdr + SMB2_TREE_DISCONNECT_STRUC_SIZE > end) - return; + *str = DCE2_Smb2FindSidInSsd(ssd, sid); + if (!*str) + return false; - structure_size = alignedNtohs(&(smb_tree_disconnect_hdr->structure_size)); + *ttr = (*str)->findTtracker(tid); + if (!*ttr) + return false; - if (structure_size == SMB2_TREE_DISCONNECT_STRUC_SIZE) - { - DCE2_Smb2RemoveTid(ssd, Smb2Tid(smb_hdr)); - } + return true; } -/******************************************************************** - * - * Process create request, first command for a file processing - * Update file name - * - ********************************************************************/ -static void DCE2_Smb2CreateRequest(DCE2_SmbSsnData* ssd, const Smb2Hdr*, - const Smb2CreateRequestHdr* smb_create_hdr,const uint8_t* end) -{ - uint16_t name_offset = alignedNtohs(&(smb_create_hdr->name_offset)); - - DCE2_Smb2InitFileTracker(&ssd->ftracker, false, 0); - - if (name_offset > SMB2_HEADER_LENGTH) - { - uint16_t size; - const uint8_t* file_data = (const uint8_t*)smb_create_hdr + smb_create_hdr->name_offset - - SMB2_HEADER_LENGTH; - if (file_data >= end) - return; - size = alignedNtohs(&(smb_create_hdr->name_length)); - if (!size || (file_data + size > end)) - return; - if (ssd->ftracker.file_name) - { - snort_free((void*)ssd->ftracker.file_name); - ssd->ftracker.file_name_size = 0; - } - ssd->ftracker.file_name = DCE2_SmbGetFileName(file_data, size, true, - &ssd->ftracker.file_name_size); - } -} +// FIXIT-L port fileCache related code along with +// DCE2_Smb2Init, DCE2_Smb2Close and DCE2_Smb2UpdateStats -/******************************************************************** - * - * Process create response, need to update file id - * For downloading, file size is decided here - * - ********************************************************************/ -static void DCE2_Smb2CreateResponse(DCE2_SmbSsnData* ssd, const Smb2Hdr*, - const Smb2CreateResponseHdr* smb_create_hdr, const uint8_t*) +static void DCE2_Smb2Inspect(DCE2_Smb2SsnData* ssd, const Smb2Hdr* smb_hdr, + const uint8_t* end) { - uint64_t fileId_persistent; - uint64_t file_size = UNKNOWN_FILE_SIZE; + const uint8_t* smb_data = (const uint8_t*)smb_hdr + SMB2_HEADER_LENGTH; + uint16_t command = alignedNtohs(&(smb_hdr->command)); + DCE2_Smb2SessionTracker* str = nullptr; + DCE2_Smb2TreeTracker* ttr = nullptr; - fileId_persistent = alignedNtohq((const uint64_t*)(&(smb_create_hdr->fileId_persistent))); - ssd->ftracker.fid_v2 = fileId_persistent; - if (smb_create_hdr->end_of_file) - { - file_size = alignedNtohq((const uint64_t*)(&(smb_create_hdr->end_of_file))); - ssd->ftracker.tracker.file.file_size = file_size; - } + uint64_t mid = Smb2Mid(smb_hdr); + uint64_t sid = Smb2Sid(smb_hdr); + uint32_t tid = Smb2Tid(smb_hdr); - if (ssd->ftracker.file_name && ssd->ftracker.file_name_size) + switch (command) { - FileContext* file = get_file_context(ssd->ftracker.fid_v2); - if (file) + case SMB2_COM_CREATE: + dce2_smb_stats.v2_crt++; + DCE2_Smb2Create(ssd, smb_hdr, smb_data, end, mid, sid, tid); + break; + case SMB2_COM_READ: + dce2_smb_stats.v2_read++; + if (!DCE2_Smb2FindSidTid(ssd, sid, tid, &str, &ttr) or + SMB2_SHARE_TYPE_DISK != ttr->get_share_type()) { - file->set_file_size(file_size); - file->set_file_name(ssd->ftracker.file_name, ssd->ftracker.file_name_size); - } - } - DCE2_Smb2ResetFileName(&(ssd->ftracker)); -} - -/******************************************************************** - * - * Process create command - * - ********************************************************************/ -static void DCE2_Smb2Create(DCE2_SmbSsnData* ssd, const Smb2Hdr* smb_hdr, - const uint8_t* smb_data, const uint8_t* end) -{ - uint16_t structure_size; - const Smb2CreateRequestHdr* smb_create_hdr = (const Smb2CreateRequestHdr*)smb_data; - - structure_size = alignedNtohs(&(smb_create_hdr->structure_size)); - - /* Using structure size to decide whether it is response or request */ - if (structure_size == SMB2_CREATE_REQUEST_STRUC_SIZE) - { - if ((const uint8_t*)smb_create_hdr + SMB2_CREATE_REQUEST_STRUC_SIZE - 1 > end) - return; - DCE2_Smb2CreateRequest(ssd, smb_hdr, smb_create_hdr, end); - } - else if (structure_size == SMB2_CREATE_RESPONSE_STRUC_SIZE) - { - if ((const uint8_t*)smb_create_hdr + SMB2_CREATE_RESPONSE_STRUC_SIZE -1 > end) - return; - DCE2_Smb2CreateResponse(ssd, smb_hdr, (const Smb2CreateResponseHdr*)smb_create_hdr, end); - } - else if (structure_size == SMB2_ERROR_RESPONSE_STRUC_SIZE) - { - const Smb2ErrorResponseHdr* smb_err_response_hdr = (const Smb2ErrorResponseHdr*)smb_data; - if ((const uint8_t*)smb_create_hdr + SMB2_ERROR_RESPONSE_STRUC_SIZE - 1 > end) + dce2_smb_stats.v2_read_ignored++; return; - /* client will ignore when byte count is 0 */ - if (smb_err_response_hdr->byte_count) - { - /*Response error, clean up request state*/ - DCE2_Smb2FreeFileName(&(ssd->ftracker)); } - } -} - -/******************************************************************** - * - * Process close command - * For some upload, file_size is decided here. - * - ********************************************************************/ -static void DCE2_Smb2CloseCmd(DCE2_SmbSsnData* ssd, const Smb2Hdr*, - const uint8_t* smb_data, const uint8_t* end) -{ - /* Using structure size to decide whether it is response or request*/ - uint16_t structure_size; - const Smb2CloseRequestHdr* smb_close_hdr = (const Smb2CloseRequestHdr*)smb_data; - - if ((const uint8_t*)smb_close_hdr + SMB2_CLOSE_REQUEST_STRUC_SIZE > end) - return; - - structure_size = alignedNtohs(&(smb_close_hdr->structure_size)); - if ((structure_size == SMB2_CLOSE_REQUEST_STRUC_SIZE) && - !ssd->ftracker.tracker.file.file_size - && ssd->ftracker.tracker.file.file_offset) - { - FileDirection dir = DetectionEngine::get_current_packet()->is_from_client() ? - FILE_UPLOAD : FILE_DOWNLOAD; - - ssd->ftracker.tracker.file.file_size = ssd->ftracker.tracker.file.file_offset; - uint64_t fileId_persistent = alignedNtohq(&(smb_close_hdr->fileId_persistent)); - FileContext* file = get_file_context(fileId_persistent); - if (file) + DCE2_Smb2Read(ssd, smb_hdr, smb_data, end, str, ttr, mid); + break; + case SMB2_COM_WRITE: + dce2_smb_stats.v2_wrt++; + if (!DCE2_Smb2FindSidTid(ssd, sid, tid, &str, &ttr) or + SMB2_SHARE_TYPE_DISK != ttr->get_share_type()) { - file->set_file_size(ssd->ftracker.tracker.file.file_size); + dce2_smb_stats.v2_wrt_ignored++; + return; } - DCE2_Smb2ProcessFileData(ssd, nullptr, 0, dir); - } - // FIXIT-L Close should probably remove file contexts from FileFlows -} - -/******************************************************************** - * - * Process set info command - * For upload, file_size is decided here. - * - ********************************************************************/ -static void DCE2_Smb2SetInfo(DCE2_SmbSsnData* ssd, const Smb2Hdr*, - const uint8_t* smb_data, const uint8_t* end) -{ - /* Using structure size to decide whether it is response or request*/ - uint16_t structure_size; - const Smb2SetInfoRequestHdr* smb_set_info_hdr = (const Smb2SetInfoRequestHdr*)smb_data; - - if ((const uint8_t*)smb_set_info_hdr + SMB2_SET_INFO_REQUEST_STRUC_SIZE > end) - return; - - structure_size = alignedNtohs(&(smb_set_info_hdr->structure_size)); - - if (structure_size == SMB2_SET_INFO_REQUEST_STRUC_SIZE) - { - const uint8_t* file_data = (const uint8_t*)smb_set_info_hdr + - SMB2_SET_INFO_REQUEST_STRUC_SIZE - 1; - if (smb_set_info_hdr->file_info_class == SMB2_FILE_ENDOFFILE_INFO) + DCE2_Smb2Write(ssd, smb_hdr, smb_data, end, str, ttr, mid); + break; + case SMB2_COM_SET_INFO: + dce2_smb_stats.v2_stinf++; + if (!DCE2_Smb2FindSidTid(ssd, sid, tid, &str, &ttr) or + SMB2_SHARE_TYPE_DISK != ttr->get_share_type()) { - uint64_t file_size = alignedNtohq((const uint64_t*)file_data); - ssd->ftracker.tracker.file.file_size = file_size; - uint64_t fileId_persistent = alignedNtohq(&(smb_set_info_hdr->fileId_persistent)); - FileContext* file = get_file_context(fileId_persistent); - if (file) - { - file->set_file_size(ssd->ftracker.tracker.file.file_size); - } - } - } -} - -/******************************************************************** - * - * Process read request - * - ********************************************************************/ -static void DCE2_Smb2ReadRequest(DCE2_SmbSsnData* ssd, const Smb2Hdr* smb_hdr, - const Smb2ReadRequestHdr* smb_read_hdr, const uint8_t*) -{ - uint64_t message_id, offset; - uint64_t fileId_persistent; - - message_id = alignedNtohq((const uint64_t*)(&(smb_hdr->message_id))); - offset = alignedNtohq((const uint64_t*)(&(smb_read_hdr->offset))); - fileId_persistent = alignedNtohq((const uint64_t*)(&(smb_read_hdr->fileId_persistent))); - DCE2_Smb2StoreRequest(ssd, message_id, offset, fileId_persistent); - if (fileId_persistent && (ssd->ftracker.fid_v2 != fileId_persistent)) - { - ssd->ftracker.fid_v2 = fileId_persistent; - } - if (ssd->ftracker.tracker.file.file_size && (offset > ssd->ftracker.tracker.file.file_size)) - { - dce_alert(GID_DCE2, DCE2_SMB_INVALID_FILE_OFFSET, (dce2CommonStats*)&dce2_smb_stats, - ssd->sd); - } -} - -/******************************************************************** - * - * Process read response - * - ********************************************************************/ -static void DCE2_Smb2ReadResponse(DCE2_SmbSsnData* ssd, const Smb2Hdr* smb_hdr, - const Smb2ReadResponseHdr* smb_read_hdr, const uint8_t* end) -{ - const uint8_t* file_data = (const uint8_t*)smb_read_hdr + SMB2_READ_RESPONSE_STRUC_SIZE - 1; - int data_size = end - file_data; - uint32_t total_data_length; - uint64_t message_id; - uint16_t data_offset; - Smb2Request* request; - - message_id = alignedNtohq((const uint64_t*)(&(smb_hdr->message_id))); - request = DCE2_Smb2GetRequest(ssd, message_id); - if (!request) - { - return; - } - data_offset = alignedNtohs((const uint16_t*)(&(smb_read_hdr->data_offset))); - if (data_offset + (const uint8_t*)smb_hdr > end) - { - dce_alert(GID_DCE2, DCE2_SMB_BAD_OFF, (dce2CommonStats*)&dce2_smb_stats, ssd->sd); - } - - ssd->ftracker.tracker.file.file_offset = request->offset; - ssd->ftracker.fid_v2 = request->file_id; - ssd->ftracker.tracker.file.file_direction = DCE2_SMB_FILE_DIRECTION__DOWNLOAD; - - DCE2_Smb2RemoveRequest(ssd, request); - - DCE2_Smb2ProcessFileData(ssd, file_data, data_size, FILE_DOWNLOAD); - ssd->ftracker.tracker.file.file_offset += data_size; - total_data_length = alignedNtohl((const uint32_t*)&(smb_read_hdr->length)); - if (total_data_length > (uint32_t)data_size) - ssd->pdu_state = DCE2_SMB_PDU_STATE__RAW_DATA; -} - -/******************************************************************** - * - * Process read command - * - ********************************************************************/ -static void DCE2_Smb2Read(DCE2_SmbSsnData* ssd, const Smb2Hdr* smb_hdr, - const uint8_t* smb_data, const uint8_t* end) -{ - uint16_t structure_size; - const Smb2ReadRequestHdr* smb_read_hdr = (const Smb2ReadRequestHdr*)smb_data; - structure_size = alignedNtohs(&(smb_read_hdr->structure_size)); - - /* Using structure size to decide whether it is response or request*/ - if (structure_size == SMB2_READ_REQUEST_STRUC_SIZE) - { - if ((const uint8_t*)smb_read_hdr + SMB2_READ_REQUEST_STRUC_SIZE - 1 > end) - return; - DCE2_Smb2ReadRequest(ssd, smb_hdr, smb_read_hdr, end); - } - else if (structure_size == SMB2_READ_RESPONSE_STRUC_SIZE) - { - if ((const uint8_t*)smb_read_hdr + SMB2_READ_RESPONSE_STRUC_SIZE - 1 > end) + dce2_smb_stats.v2_stinf_ignored++; return; - DCE2_Smb2ReadResponse(ssd, smb_hdr, (const Smb2ReadResponseHdr*)smb_read_hdr, end); - } - else - { - uint64_t message_id; - Smb2Request* request; + } - message_id = alignedNtohq((const uint64_t*)(&(smb_hdr->message_id))); - request = DCE2_Smb2GetRequest(ssd, message_id); - if (!request) + DCE2_Smb2SetInfo(ssd, smb_hdr, smb_data, end, ttr); + break; + case SMB2_COM_CLOSE: + dce2_smb_stats.v2_cls++; + if (!DCE2_Smb2FindSidTid(ssd, sid, tid, &str, &ttr) or + SMB2_SHARE_TYPE_DISK != ttr->get_share_type()) { + dce2_smb_stats.v2_cls_ignored++; return; } - DCE2_Smb2RemoveRequest(ssd, request); - } -} - -/******************************************************************** - * - * Process write request - * - ********************************************************************/ -static void DCE2_Smb2WriteRequest(DCE2_SmbSsnData* ssd, const Smb2Hdr* smb_hdr, - const Smb2WriteRequestHdr* smb_write_hdr, const uint8_t* end) -{ - const uint8_t* file_data = (const uint8_t*)smb_write_hdr + SMB2_WRITE_REQUEST_STRUC_SIZE - 1; - int data_size = end - file_data; - uint64_t fileId_persistent, offset; - uint16_t data_offset; - uint32_t total_data_length; - - fileId_persistent = alignedNtohq((const uint64_t*)(&(smb_write_hdr->fileId_persistent))); - if (fileId_persistent && (ssd->ftracker.fid_v2 != fileId_persistent)) - { - ssd->ftracker.fid_v2 = fileId_persistent; - } - data_offset = alignedNtohs((const uint16_t*)(&(smb_write_hdr->data_offset))); - if (data_offset + (const uint8_t*)smb_hdr > end) - { - dce_alert(GID_DCE2, DCE2_SMB_BAD_OFF, (dce2CommonStats*)&dce2_smb_stats, ssd->sd); - } - offset = alignedNtohq((const uint64_t*)(&(smb_write_hdr->offset))); - if (ssd->ftracker.tracker.file.file_size && (offset > ssd->ftracker.tracker.file.file_size)) - { - dce_alert(GID_DCE2, DCE2_SMB_INVALID_FILE_OFFSET, (dce2CommonStats*)&dce2_smb_stats, - ssd->sd); - } - ssd->ftracker.tracker.file.file_direction = DCE2_SMB_FILE_DIRECTION__UPLOAD; - ssd->ftracker.tracker.file.file_offset = offset; - - DCE2_Smb2ProcessFileData(ssd, file_data, data_size, FILE_UPLOAD); - ssd->ftracker.tracker.file.file_offset += data_size; - total_data_length = alignedNtohl((const uint32_t*)&(smb_write_hdr->length)); - if (total_data_length > (uint32_t)data_size) - ssd->pdu_state = DCE2_SMB_PDU_STATE__RAW_DATA; -} - -/******************************************************************** - * - * Process write command - * - ********************************************************************/ -static void DCE2_Smb2Write(DCE2_SmbSsnData* ssd, const Smb2Hdr* smb_hdr, - const uint8_t* smb_data, const uint8_t* end) -{ - uint16_t structure_size; - const Smb2WriteRequestHdr* smb_write_hdr = (const Smb2WriteRequestHdr*)smb_data; - structure_size = alignedNtohs(&(smb_write_hdr->structure_size)); - - /* Using structure size to decide whether it is response or request*/ - if (structure_size == SMB2_WRITE_REQUEST_STRUC_SIZE) - { - if ((const uint8_t*)smb_write_hdr + SMB2_WRITE_REQUEST_STRUC_SIZE - 1 > end) - return; - DCE2_Smb2WriteRequest(ssd, smb_hdr, smb_write_hdr, end); - } -} - -/******************************************************************** - * - * Purpose: - * Process SMB2 commands. - * - * Arguments: - * DCE2_SmbSsnData * - the session data structure. - * const Smb2Hdr * - pointer to the SMB2 header. - * const uint8_t * - pointer to end of payload. - * Returns: None - * - ********************************************************************/ -static void DCE2_Smb2Inspect(DCE2_SmbSsnData* ssd, const Smb2Hdr* smb_hdr, const uint8_t* end) -{ - const uint8_t* smb_data = (const uint8_t*)smb_hdr + SMB2_HEADER_LENGTH; - uint16_t command = alignedNtohs(&(smb_hdr->command)); - uint8_t share_type = DCE2_Smb2GetShareType(ssd, Smb2Tid(smb_hdr)); - switch (command) - { - case SMB2_COM_CREATE: - dce2_smb_stats.smb2_create++; - - if ( SMB2_SHARE_TYPE_NONE == share_type ) - { - if ( DCE2_RET__SUCCESS == - DCE2_Smb2InsertTid(ssd, Smb2Tid(smb_hdr), SMB2_SHARE_TYPE_DISK) ) - DCE2_Smb2Create(ssd, smb_hdr, smb_data, end); - } - else if ( SMB2_SHARE_TYPE_DISK == share_type ) - { - DCE2_Smb2Create(ssd, smb_hdr, smb_data, end); - } - else + DCE2_Smb2CloseCmd(ssd, smb_hdr, smb_data, end, ttr); + break; + case SMB2_COM_TREE_CONNECT: + dce2_smb_stats.v2_tree_cnct++; + // This will always return session tracker + str = DCE2_Smb2FindElseCreateSid(ssd, sid); + if (str) { - debug_logf(dce_smb_trace, nullptr, "Not handling create request for IPC with TID (%u)\n", - Smb2Tid(smb_hdr)); + DCE2_Smb2TreeConnect(ssd, smb_hdr, smb_data, end, str, tid); } break; - case SMB2_COM_READ: - dce2_smb_stats.smb2_read++; - if ( SMB2_SHARE_TYPE_DISK != share_type ) - return; - DCE2_Smb2Read(ssd, smb_hdr, smb_data, end); - break; - case SMB2_COM_WRITE: - dce2_smb_stats.smb2_write++; - if ( SMB2_SHARE_TYPE_DISK != share_type ) - return; - DCE2_Smb2Write(ssd, smb_hdr, smb_data, end); - break; - case SMB2_COM_SET_INFO: - dce2_smb_stats.smb2_set_info++; - if ( SMB2_SHARE_TYPE_DISK != share_type ) - return; - DCE2_Smb2SetInfo(ssd, smb_hdr, smb_data, end); - break; - case SMB2_COM_CLOSE: - dce2_smb_stats.smb2_close++; - if ( SMB2_SHARE_TYPE_DISK != share_type ) + case SMB2_COM_TREE_DISCONNECT: + dce2_smb_stats.v2_tree_discn++; + if (!DCE2_Smb2FindSidTid(ssd, sid, tid, &str, &ttr)) + { + dce2_smb_stats.v2_tree_discn_ignored++; return; - DCE2_Smb2CloseCmd(ssd, smb_hdr, smb_data, end); + } + DCE2_Smb2TreeDisconnect(ssd, smb_data, end, str, tid); break; - case SMB2_COM_TREE_CONNECT: - dce2_smb_stats.smb2_tree_connect++; - DCE2_Smb2TreeConnect(ssd, smb_hdr, smb_data, end); + case SMB2_COM_SESSION_SETUP: + dce2_smb_stats.v2_setup++; + DCE2_Smb2Setup(ssd, smb_hdr, sid, smb_data, end); break; - case SMB2_COM_TREE_DISCONNECT: - dce2_smb_stats.smb2_tree_disconnect++; - DCE2_Smb2TreeDisconnect(ssd, smb_hdr, smb_data, end); + case SMB2_COM_LOGOFF: + dce2_smb_stats.v2_logoff++; + DCE2_Smb2Logoff(ssd, smb_data, sid); break; default: + dce2_smb_stats.v2_msgs_uninspected++; break; } } // This is the main entry point for SMB2 processing. -void DCE2_Smb2Process(DCE2_SmbSsnData* ssd) +void DCE2_Smb2Process(DCE2_Smb2SsnData* ssd) { Packet* p = DetectionEngine::get_current_packet(); const uint8_t* data_ptr = p->data; uint16_t data_len = p->dsize; - /*Check header length*/ + // Check header length if (data_len < sizeof(NbssHdr) + SMB2_HEADER_LENGTH) - return; - - if (!ssd->ftracker.is_smb2) { - DCE2_Smb2InitFileTracker(&(ssd->ftracker), false, 0); + dce2_smb_stats.v2_hdr_err++; + return; } - /* Process the header */ + // Process the header if (p->is_pdu_start()) { const Smb2Hdr* smb_hdr = (const Smb2Hdr*)(data_ptr + sizeof(NbssHdr)); uint32_t next_command_offset; - /* SMB protocol allows multiple smb commands to be grouped in a single packet. - So loop through to parse all the smb commands. - Reference: https://msdn.microsoft.com/en-us/library/cc246614.aspx - "A nonzero value for the NextCommand field in the SMB2 header indicates a compound - request. NextCommand in the SMB2 header of a request specifies an offset, in bytes, - from the beginning of the SMB2 header under consideration to the start of the 8-byte - aligned SMB2 header of the subsequent request. Such compounding can be used to append - multiple requests up to the maximum size<88> that is supported by the transport." */ + uint8_t compound_request_index = 0; + // SMB protocol allows multiple smb commands to be grouped in a single packet. + // So loop through to parse all the smb commands. + // Reference: https://msdn.microsoft.com/en-us/library/cc246614.aspx + // "A nonzero value for the NextCommand field in the SMB2 header indicates a compound + // request. NextCommand in the SMB2 header of a request specifies an offset, in bytes, + // from the beginning of the SMB2 header under consideration to the start of the 8-byte + // aligned SMB2 header of the subsequent request. Such compounding can be used to append + // multiple requests up to the maximum size<88> that is supported by the transport." do { DCE2_Smb2Inspect(ssd, smb_hdr, data_ptr + data_len); - /* In case of message compounding, find the offset of the next smb command */ + // In case of message compounding, find the offset of the next smb command next_command_offset = alignedNtohl(&(smb_hdr->next_command)); if (next_command_offset + (const uint8_t*)smb_hdr > (data_ptr + data_len)) { dce_alert(GID_DCE2, DCE2_SMB_BAD_NEXT_COMMAND_OFFSET, (dce2CommonStats*)&dce2_smb_stats, ssd->sd); - + dce2_smb_stats.v2_bad_next_cmd_offset++; return; } if (next_command_offset) { smb_hdr = (const Smb2Hdr*)((const uint8_t*)smb_hdr + next_command_offset); + compound_request_index++; + } + + if (compound_request_index > DCE2_ScSmbMaxCompound((dce2SmbProtoConf*)ssd->sd.config)) + { + dce2_smb_stats.v2_cmpnd_req_lt_crossed++; + return; } } - while (next_command_offset && smb_hdr); + while (next_command_offset and smb_hdr); } - else if (ssd->pdu_state == DCE2_SMB_PDU_STATE__RAW_DATA) + else if ( ssd->ftracker_tcp and (ssd->ftracker_tcp->smb2_pdu_state == + DCE2_SMB_PDU_STATE__RAW_DATA)) { - /*continue processing raw data*/ + debug_logf(dce_smb_trace, nullptr, "Processing raw data\n"); + // continue processing raw data FileDirection dir = p->is_from_client() ? FILE_UPLOAD : FILE_DOWNLOAD; DCE2_Smb2ProcessFileData(ssd, data_ptr, data_len, dir); - ssd->ftracker.tracker.file.file_offset += data_len; + ssd->ftracker_tcp->file_offset += data_len; } } -/* Initialize smb2 file tracker */ -DCE2_Ret DCE2_Smb2InitFileTracker(DCE2_SmbFileTracker* ftracker, - const bool is_ipc, const uint64_t fid) +static inline void DCE2_Smb2FreeSessionData(void* str) { - if (ftracker == nullptr) - return DCE2_RET__ERROR; - - DCE2_Smb2FreeFileName(ftracker); - ftracker->fid_v2 = fid; - ftracker->is_ipc = is_ipc; - ftracker->is_smb2 = true; - - ftracker->ff_file_size = 0; - ftracker->ff_file_offset = 0; - ftracker->ff_file_direction = DCE2_SMB_FILE_DIRECTION__UNKNOWN; + DCE2_Smb2SessionTracker* stracker = (DCE2_Smb2SessionTracker*)str; + DCE2_SmbSessionCacheRemove(stracker->get_session_id()); +} +DCE2_Ret DCE2_Smb2InitData(DCE2_Smb2SsnData* ssd) +{ + memset(&ssd->sd, 0, sizeof(DCE2_SsnData)); + ssd->session_trackers.Init(DCE2_Smb2FreeSessionData); + memset(&ssd->policy, 0, sizeof(DCE2_Policy)); + ssd->dialect_index = 0; + ssd->ssn_state_flags = 0; + ssd->ftracker_tcp = nullptr; + ssd->max_file_depth = FileService::get_max_file_depth(); return DCE2_RET__SUCCESS; } -/* Check whether the packet is smb2 */ +// Check whether the packet is smb2 DCE2_SmbVersion DCE2_Smb2Version(const Packet* p) { - /* Only check reassembled SMB2 packet*/ + // Only check reassembled SMB2 packet if ( p->has_paf_payload() and (p->dsize > sizeof(NbssHdr) + DCE2_SMB_ID_SIZE) ) // DCE2_SMB_ID is u32 { @@ -808,24 +235,10 @@ DCE2_SmbVersion DCE2_Smb2Version(const Packet* p) uint32_t smb_version_id = SmbId((const SmbNtHdr*)smb_hdr); if (smb_version_id == DCE2_SMB_ID) - return DCE2_SMB_VERISON_1; - + return DCE2_SMB_VERSION_1; else if (smb_version_id == DCE2_SMB2_ID) - return DCE2_SMB_VERISON_2; + return DCE2_SMB_VERSION_2; } - return DCE2_SMB_VERISON_NULL; -} - -void DCE2_Smb2CleanRequests(Smb2Request* requests) -{ - Smb2Request* request = requests; - while (request) - { - Smb2Request* next; - next = request->next; - snort_free((void*)request); - request = next; - } + return DCE2_SMB_VERSION_NULL; } - diff --git a/src/service_inspectors/dce_rpc/dce_smb2.h b/src/service_inspectors/dce_rpc/dce_smb2.h index 8bc2fdb28..f4af263ce 100644 --- a/src/service_inspectors/dce_rpc/dce_smb2.h +++ b/src/service_inspectors/dce_rpc/dce_smb2.h @@ -22,9 +22,9 @@ #ifndef DCE_SMB2_H #define DCE_SMB2_H +#include "dce_db.h" #include "dce_smb.h" - -#define SMB2_FLAGS_ASYNC_COMMAND 0x00000002 +#include "utils/util.h" struct Smb2Hdr { @@ -83,6 +83,237 @@ struct Smb2ErrorResponseHdr uint8_t error_data[1]; /* If byte_count is 0, this MUST be 0*/ }; +class DCE2_Smb2TreeTracker; + +class DCE2_Smb2RequestTracker +{ +public: + + DCE2_Smb2RequestTracker() = delete; + DCE2_Smb2RequestTracker(const DCE2_Smb2RequestTracker& arg) = delete; + DCE2_Smb2RequestTracker& operator=(const DCE2_Smb2RequestTracker& arg) = delete; + + DCE2_Smb2RequestTracker(uint64_t offset_v, uint64_t file_id_v, + char* fname_v, uint16_t fname_len_v, DCE2_Smb2TreeTracker *ttr) : + fname_len(fname_len_v), fname(fname_v), offset(offset_v), + file_id(file_id_v), tree_trk(ttr) + { + // fname allocated by DCE2_SmbGetFileName + } + + ~DCE2_Smb2RequestTracker() + { + if (fname != nullptr) + { + snort_free((void*)fname); + } + } + + uint16_t get_file_name_len() { return fname_len; } + char* get_file_name() { return fname; } + uint64_t get_offset() { return offset; } + uint64_t get_file_id() { return file_id; } + DCE2_Smb2TreeTracker* get_tree_tracker() { return tree_trk; } + +private: + + uint16_t fname_len; + char* fname; + uint64_t offset; + uint64_t file_id; + DCE2_Smb2TreeTracker* tree_trk; +}; + +class DCE2_Smb2FileTracker +{ +public: + + DCE2_Smb2FileTracker() = delete; + DCE2_Smb2FileTracker(const DCE2_Smb2FileTracker& arg) = delete; + DCE2_Smb2FileTracker& operator=(const DCE2_Smb2FileTracker& arg) = delete; + + DCE2_Smb2FileTracker(uint64_t file_id_v, char* file_name_v, + uint64_t file_size_v) : file_id(file_id_v), file_size(file_size_v) + { + if (file_name_v) + file_name.assign(file_name_v); + + file_offset = 0; + bytes_processed = 0; + } + + ~DCE2_Smb2FileTracker() + { + // Nothing to be done + } + + uint64_t bytes_processed; + uint64_t file_offset; + uint64_t file_id; + uint64_t file_size = 0; + std::string file_name; + DCE2_SmbPduState smb2_pdu_state; +}; + + +typedef DCE2_DbMap > DCE2_DbMapFtracker; +typedef DCE2_DbMap > DCE2_DbMapRtracker; +class DCE2_Smb2TreeTracker +{ +public: + + DCE2_Smb2TreeTracker() = delete; + DCE2_Smb2TreeTracker(const DCE2_Smb2TreeTracker& arg) = delete; + DCE2_Smb2TreeTracker& operator=(const DCE2_Smb2TreeTracker& arg) = delete; + + DCE2_Smb2TreeTracker (uint32_t tid_v, uint8_t share_type_v) : share_type( + share_type_v), tid(tid_v) + { + } + + DCE2_Smb2FileTracker* findFtracker(uint64_t file_id) + { + return file_trackers.Find(file_id); + } + + void insertFtracker(uint64_t file_id, DCE2_Smb2FileTracker* ftracker) + { + file_trackers.Insert(file_id, ftracker); + } + + void removeFtracker(uint64_t file_id) + { + removeDataRtrackerWithFid(file_id); + file_trackers.Remove(file_id); + } + + DCE2_Smb2RequestTracker* findDataRtracker(uint64_t message_id) + { + return request_trackers.Find(message_id); + } + + void insertDataRtracker(uint64_t message_id, DCE2_Smb2RequestTracker* readtracker) + { + request_trackers.Insert(message_id, readtracker); + } + + void removeDataRtracker(uint64_t message_id) + { + if (findDataRtracker(message_id)) + { + request_trackers.Remove(message_id); + } + } + + void removeDataRtrackerWithFid(uint64_t fid) + { + auto all_requests = request_trackers.get_all_entry(); + for ( auto & h : all_requests ) + { + if (h.second->get_file_id() == fid) + removeDataRtracker(h.first); // this is message id + } + } + + int getDataRtrackerSize() + { + return request_trackers.GetSize(); + } + + uint8_t get_share_type() { return share_type; } + uint32_t get_tid() { return tid; } +private: + uint8_t share_type; + uint32_t tid; + + DCE2_DbMapRtracker request_trackers; + DCE2_DbMapFtracker file_trackers; +}; + +typedef DCE2_DbMap > DCE2_DbMapTtracker; +class DCE2_Smb2SessionTracker +{ +public: + + DCE2_Smb2SessionTracker() { } + + void insertTtracker(uint32_t tree_id, DCE2_Smb2TreeTracker* ttr) + { + tree_trackers.Insert(tree_id, ttr); + } + + DCE2_Smb2TreeTracker* findTtracker(uint32_t tree_id) + { + return tree_trackers.Find(tree_id); + } + + void removeTtracker(uint32_t tree_id) + { + // Remove any dangling request trackers with tree id + removeRtrackerWithTid(tree_id); + tree_trackers.Remove(tree_id); + } + + DCE2_Smb2RequestTracker* findRtracker(uint64_t mid) + { + return create_request_trackers.Find(mid); + } + + void insertRtracker(uint64_t message_id, DCE2_Smb2RequestTracker* rtracker) + { + create_request_trackers.Insert(message_id, rtracker); + } + + void removeRtracker(uint64_t message_id) + { + create_request_trackers.Remove(message_id); + } + + void removeRtrackerWithTid(uint32_t tid) + { + auto all_requests = create_request_trackers.get_all_entry(); + for ( auto & h : all_requests ) + { + if (h.second->get_tree_tracker() and h.second->get_tree_tracker()->get_tid() == tid) + removeRtracker(h.first); // this is message id + } + } + + uint16_t getTotalRequestsPending() + { + uint16_t total_count = 0; + auto all_tree_trackers = tree_trackers.get_all_entry(); + for ( auto & h : all_tree_trackers ) + { + total_count += h.second->getDataRtrackerSize(); // all read/write + } + total_count += create_request_trackers.GetSize(); // all create + return total_count; + } + + void set_session_id(uint64_t sid) { session_id = sid; } + uint64_t get_session_id() { return session_id; } + +private: + uint64_t session_id; + DCE2_DbMapTtracker tree_trackers; + DCE2_DbMapRtracker create_request_trackers; +}; + +typedef DCE2_DbMap > DCE2_DbMapStracker; +struct DCE2_Smb2SsnData +{ + DCE2_SsnData sd; // This member must be first + uint8_t smb_id; + DCE2_Policy policy; + int dialect_index; + int ssn_state_flags; + int64_t max_file_depth; // Maximum file depth as returned from file API + int16_t max_outstanding_requests; // Maximum number of request that can stay pending + DCE2_DbMapStracker session_trackers; + DCE2_Smb2FileTracker* ftracker_tcp; //To keep tab of current file being transferred over TCP +}; + /* SMB2 command codes */ #define SMB2_COM_NEGOTIATE 0x00 #define SMB2_COM_SESSION_SETUP 0x01 @@ -216,7 +447,6 @@ struct Smb2CloseRequestHdr uint64_t fileId_volatile; /* fileId that is volatile */ }; -#define SMB2_SHARE_TYPE_NONE 0x00 #define SMB2_SHARE_TYPE_DISK 0x01 #define SMB2_SHARE_TYPE_PIPE 0x02 #define SMB2_SHARE_TYPE_PRINT 0x03 @@ -237,6 +467,26 @@ struct Smb2TreeDisConnectHdr uint16_t reserved; /* reserved */ }; +struct Smb2SetupRequestHdr +{ + uint16_t structure_size; /* This MUST be set to 25 (0x19) bytes */ + uint8_t flags; + uint8_t security_mode; + uint32_t capabilities; + uint32_t channel; + uint16_t secblob_ofs; + uint16_t secblob_size; + uint64_t previous_sessionid; +}; + +struct Smb2SetupResponseHdr +{ + uint16_t structure_size; /* This MUST be set to 9 (0x09) bytes */ + uint16_t session_flags; + uint16_t secblob_ofs; + uint16_t secblob_size; +}; + #define SMB2_HEADER_LENGTH 64 #define SMB2_ERROR_RESPONSE_STRUC_SIZE 9 @@ -246,6 +496,7 @@ struct Smb2TreeDisConnectHdr #define SMB2_CREATE_REQUEST_DATA_OFFSET 120 #define SMB2_CLOSE_REQUEST_STRUC_SIZE 24 +#define SMB2_CLOSE_RESPONSE_STRUC_SIZE 60 #define SMB2_WRITE_REQUEST_STRUC_SIZE 49 #define SMB2_WRITE_RESPONSE_STRUC_SIZE 17 @@ -256,20 +507,19 @@ struct Smb2TreeDisConnectHdr #define SMB2_SET_INFO_REQUEST_STRUC_SIZE 33 #define SMB2_SET_INFO_RESPONSE_STRUC_SIZE 2 +#define SMB2_TREE_CONNECT_REQUEST_STRUC_SIZE 9 #define SMB2_TREE_CONNECT_RESPONSE_STRUC_SIZE 16 -#define SMB2_TREE_DISCONNECT_STRUC_SIZE 4 +#define SMB2_TREE_DISCONNECT_REQUEST_STRUC_SIZE 4 #define SMB2_FILE_ENDOFFILE_INFO 0x14 -/* Clean up all the pending requests*/ -void DCE2_Smb2CleanRequests(Smb2Request* requests); +#define SMB2_SETUP_REQUEST_STRUC_SIZE 25 +#define SMB2_SETUP_RESPONSE_STRUC_SIZE 9 -/* Process smb2 message */ -void DCE2_Smb2Process(DCE2_SmbSsnData* ssd); +#define SMB2_LOGOFF_REQUEST_STRUC_SIZE 4 -/* Initialize file tracker for smb2 processing */ -DCE2_Ret DCE2_Smb2InitFileTracker(DCE2_SmbFileTracker* ftracker, - const bool is_ipc, const uint64_t fid); +/* Process smb2 message */ +void DCE2_Smb2Process(DCE2_Smb2SsnData* ssd); /* Check smb version based on smb header */ DCE2_SmbVersion DCE2_Smb2Version(const snort::Packet* p); diff --git a/src/service_inspectors/dce_rpc/dce_smb2_commands.cc b/src/service_inspectors/dce_rpc/dce_smb2_commands.cc new file mode 100644 index 000000000..e149d2cb0 --- /dev/null +++ b/src/service_inspectors/dce_rpc/dce_smb2_commands.cc @@ -0,0 +1,695 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2016-2019 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_smb2_commands.cc author Bhargava Jandhyala +// based on work by Todd Wease + +// Smb commands processing + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "dce_smb2_commands.h" +#include "log/messages.h" +#include "main/snort_debug.h" +#include "packet_io/active.h" +#include "protocols/packet.h" + +using namespace snort; +#define UNKNOWN_FILE_SIZE (~0) +#define SMB2_CHECK_HDR_ERROR(smb_data, end, strcuture_size, counter)\ +{ \ + if ((smb_data + (strcuture_size)) > end)\ + {\ + counter++;\ + return;\ + }\ +} + +static inline FileContext* get_smb_file_context(uint64_t file_id) +{ + FileFlows* file_flows = FileFlows::get_file_flows(DetectionEngine::get_current_packet()->flow); + + if ( !file_flows ) + { + dce2_smb_stats.v2_inv_file_ctx_err++; + return nullptr; + } + + return file_flows->get_file_context(file_id, true); +} + +void DCE2_Smb2ProcessFileData(DCE2_Smb2SsnData* ssd, const uint8_t* file_data, + uint32_t data_size, FileDirection dir) +{ + int64_t file_detection_depth = DCE2_ScSmbFileDepth((dce2SmbProtoConf*)ssd->sd.config); + int64_t detection_size = 0; + + if (file_detection_depth == 0) + detection_size = data_size; + else if ( ssd->ftracker_tcp->file_offset < (uint64_t)file_detection_depth) + { + if ( file_detection_depth - ssd->ftracker_tcp->file_offset < data_size ) + detection_size = file_detection_depth - ssd->ftracker_tcp->file_offset; + else + detection_size = data_size; + } + + if (detection_size) + { + set_file_data(file_data, + (detection_size > UINT16_MAX) ? UINT16_MAX : (uint16_t)detection_size); + + DCE2_FileDetect(); + } + + Packet* p = DetectionEngine::get_current_packet(); + ssd->ftracker_tcp->bytes_processed += detection_size; + + // Do not process data beyond file size if file size is known. + FileFlows* file_flows = FileFlows::get_file_flows(p->flow); + if ( !file_flows or ( ssd->ftracker_tcp->file_size and + ssd->ftracker_tcp->bytes_processed > ssd->ftracker_tcp->file_size ) ) + { + dce2_smb_stats.v2_extra_file_data_err++; + return; + } + + file_flows->file_process(p, ssd->ftracker_tcp->file_id, file_data, data_size, + ssd->ftracker_tcp->file_offset, dir); +} + +//------------------------------------------------------------------------- +// Process session setup response to find/create session tracker +//------------------------------------------------------------------------- +void DCE2_Smb2Setup(DCE2_Smb2SsnData* ssd, const Smb2Hdr* smb_hdr, const uint64_t sid, + const uint8_t* smb_data, const uint8_t* end) +{ + uint16_t structure_size = alignedNtohs((const uint16_t*)smb_data); + + if (structure_size == SMB2_ERROR_RESPONSE_STRUC_SIZE and Smb2Error(smb_hdr)) + { + dce2_smb_stats.v2_setup_err_resp++; + } + else if (structure_size == SMB2_SETUP_RESPONSE_STRUC_SIZE) + { + SMB2_CHECK_HDR_ERROR( + smb_data, end, SMB2_SETUP_RESPONSE_STRUC_SIZE - 1, + dce2_smb_stats.v2_setup_resp_hdr_err) + DCE2_Smb2FindElseCreateSid(ssd, sid); + } + else if (structure_size != SMB2_SETUP_REQUEST_STRUC_SIZE) + { + dce2_smb_stats.v2_setup_inv_str_sz++; + } +} + +//------------------------------------------------------------------------- +// Process tree connect response to find/create tree tracker +//------------------------------------------------------------------------- +void DCE2_Smb2TreeConnect(DCE2_Smb2SsnData* ssd, const Smb2Hdr* smb_hdr, + const uint8_t* smb_data, const uint8_t* end, DCE2_Smb2SessionTracker* str, uint32_t tid) +{ + uint16_t structure_size = alignedNtohs((const uint16_t*)smb_data); + + if (structure_size == SMB2_ERROR_RESPONSE_STRUC_SIZE and Smb2Error(smb_hdr)) + { + dce2_smb_stats.v2_tree_cnct_err_resp++; + } + else if (structure_size == SMB2_TREE_CONNECT_RESPONSE_STRUC_SIZE) + { + SMB2_CHECK_HDR_ERROR( + smb_data, end, SMB2_TREE_CONNECT_RESPONSE_STRUC_SIZE, + dce2_smb_stats.v2_tree_cnct_resp_hdr_err) + + if (!DCE2_Smb2FindElseCreateTid(ssd, tid, + ((const Smb2TreeConnectResponseHdr*)smb_data)->share_type, str)) + dce2_smb_stats.v2_tree_cnct_ignored++; + } + else if (structure_size != SMB2_TREE_CONNECT_REQUEST_STRUC_SIZE) + { + dce2_smb_stats.v2_tree_cnct_inv_str_sz++; + } +} + +//------------------------------------------------------------------------- +// Process tree disconnect request to cleanup tree tracker and its +// corresponding request trackers and file trackers +//------------------------------------------------------------------------- +void DCE2_Smb2TreeDisconnect(DCE2_Smb2SsnData*, const uint8_t* smb_data, + const uint8_t* end, DCE2_Smb2SessionTracker* str, uint32_t tid) +{ + if (SMB2_TREE_DISCONNECT_REQUEST_STRUC_SIZE == alignedNtohs((const uint16_t*)smb_data)) + { + SMB2_CHECK_HDR_ERROR( + smb_data, end, SMB2_TREE_DISCONNECT_REQUEST_STRUC_SIZE, + dce2_smb_stats.v2_tree_discn_req_hdr_err) + str->removeTtracker(tid); + } + else + { + dce2_smb_stats.v2_tree_discn_inv_str_sz++; + } +} + +//------------------------------------------------------------------------- +// Process create request to get file name and save it in request tracker +//------------------------------------------------------------------------- +static void DCE2_Smb2CreateRequest(DCE2_Smb2SsnData* ssd, + const Smb2CreateRequestHdr* smb_create_hdr,const uint8_t* end, + DCE2_Smb2SessionTracker* str, DCE2_Smb2TreeTracker* ttr, uint64_t mid) +{ + uint16_t name_offset = alignedNtohs(&(smb_create_hdr->name_offset)); + + if (name_offset > SMB2_HEADER_LENGTH) + { + uint16_t name_len = 0; + + const uint8_t* file_data = (const uint8_t*)smb_create_hdr + smb_create_hdr->name_offset - + SMB2_HEADER_LENGTH; + if (file_data >= end) + { + dce2_smb_stats.v2_crt_inv_file_data++; + return; + } + + uint16_t size = alignedNtohs(&(smb_create_hdr->name_length)); + if (!size or (file_data + size > end)) + { + dce2_smb_stats.v2_crt_inv_file_data++; + return; + } + + char* file_name = DCE2_SmbGetFileName(file_data, size, true, + &name_len); + + if (ssd->max_outstanding_requests > str->getTotalRequestsPending()) + { + DCE2_Smb2RequestTracker* rtracker = str->findRtracker(mid); + if (rtracker) // Cleanup existing tracker + str->removeRtracker(mid); + + rtracker = new DCE2_Smb2RequestTracker(0, 0, file_name, name_len, ttr); + + str->insertRtracker(mid, rtracker); + } + else + { + snort_free(file_name); + dce_alert(GID_DCE2, DCE2_SMB_MAX_REQS_EXCEEDED, (dce2CommonStats*)&dce2_smb_stats, + ssd->sd); + } + } + else + { + dce2_smb_stats.v2_crt_req_hdr_err++; + } +} + +//------------------------------------------------------------------------- +// Process create response to create file tracker with file id and file +// size. Request tracker is cleaned after updating file name in file tracker +//------------------------------------------------------------------------- +static void DCE2_Smb2CreateResponse(DCE2_Smb2SsnData*, + const Smb2CreateResponseHdr* smb_create_hdr, + DCE2_Smb2RequestTracker* rtracker) +{ + uint64_t file_size = 0; + uint64_t fileId_persistent = alignedNtohq((const uint64_t*)(&(smb_create_hdr->fileId_persistent))); + + if (smb_create_hdr->end_of_file) + { + file_size = alignedNtohq((const uint64_t*)(&(smb_create_hdr->end_of_file))); + } + + DCE2_Smb2FileTracker* ftracker = rtracker->get_tree_tracker()->findFtracker(fileId_persistent); + if (!ftracker) + { + ftracker = new DCE2_Smb2FileTracker( + fileId_persistent, rtracker->get_file_name(), file_size); + } + else // compounded create request + read request case + { + ftracker->file_name.assign(rtracker->get_file_name()); + ftracker->file_size = file_size; + } + + if (rtracker->get_file_name() and rtracker->get_file_name_len()) + { + FileContext* file = get_smb_file_context(fileId_persistent); + if (file) + { + file->set_file_size(!file_size ? UNKNOWN_FILE_SIZE : file_size); + file->set_file_name(rtracker->get_file_name(), rtracker->get_file_name_len()); + } + } + + rtracker->get_tree_tracker()->insertFtracker(fileId_persistent, ftracker); +} + +//------------------------------------------------------------------------- +// Process create request to handle mid stream sessions by adding tree +// tracker if not already present. Process create response for only disk +// share type. +//------------------------------------------------------------------------- +void DCE2_Smb2Create(DCE2_Smb2SsnData* ssd, const Smb2Hdr* smb_hdr, + const uint8_t* smb_data, const uint8_t* end, uint64_t mid, uint64_t sid, uint32_t tid) +{ + DCE2_Smb2SessionTracker* str = DCE2_Smb2FindElseCreateSid(ssd, sid); + uint16_t structure_size = alignedNtohs((const uint16_t*)smb_data); + + if (structure_size == SMB2_ERROR_RESPONSE_STRUC_SIZE and Smb2Error(smb_hdr)) + { + //in case of compound create + read, a ftracker is already created, remove it + DCE2_Smb2RequestTracker* rtr = str->findRtracker(mid); + if ( rtr and rtr->get_tree_tracker() and rtr->get_file_id() ) + { + if (ssd->ftracker_tcp->file_id == rtr->get_file_id()) + ssd->ftracker_tcp = NULL; + rtr->get_tree_tracker()->removeFtracker(rtr->get_file_id()); + } + str->removeRtracker(mid); + dce2_smb_stats.v2_crt_err_resp++; + } + // Using structure size to decide whether it is response or request + else if (structure_size == SMB2_CREATE_REQUEST_STRUC_SIZE) + { + SMB2_CHECK_HDR_ERROR( + smb_data, end, SMB2_CREATE_REQUEST_STRUC_SIZE - 1, + dce2_smb_stats.v2_crt_req_hdr_err) + + DCE2_Smb2TreeTracker* ttr = str->findTtracker(tid); + if (!ttr) + { + ttr = DCE2_Smb2InsertTid(ssd, tid, SMB2_SHARE_TYPE_DISK, str); + } + else if (SMB2_SHARE_TYPE_DISK != ttr->get_share_type()) + { + debug_logf(dce_smb_trace, nullptr, "Not handling create request for IPC with TID (%u)\n", + tid); + dce2_smb_stats.v2_crt_req_ipc++; + return; + } + DCE2_Smb2CreateRequest(ssd, (const Smb2CreateRequestHdr*)smb_data, end, str, ttr, mid); + } + else if (structure_size == SMB2_CREATE_RESPONSE_STRUC_SIZE) + { + SMB2_CHECK_HDR_ERROR( + smb_data, end, SMB2_CREATE_RESPONSE_STRUC_SIZE - 1, + dce2_smb_stats.v2_crt_resp_hdr_err) + + DCE2_Smb2RequestTracker* rtr = str->findRtracker(mid); + if (!rtr) + { + debug_logf(dce_smb_trace, nullptr, + "No create request received for this MID (%" PRIu64 ")\n", mid); + dce2_smb_stats.v2_crt_rtrkr_misng++; + return; + } + // Check required only for null tree tracker since for IPC, + // the request tracker itself is not added. + if (!rtr->get_tree_tracker()) + { + debug_logf(dce_smb_trace, nullptr, + "Tree tracker is missing for create request\n"); + dce2_smb_stats.v2_crt_tree_trkr_misng++; + str->removeRtracker(mid); + return; + } + + DCE2_Smb2CreateResponse(ssd, (const Smb2CreateResponseHdr*)smb_data, rtr); + str->removeRtracker(mid); + } + else + { + dce2_smb_stats.v2_crt_inv_str_sz++; + } +} + +//------------------------------------------------------------------------- +// Process close command request to do file processing for an upload or +// download request with unknown size. +//------------------------------------------------------------------------- +void DCE2_Smb2CloseCmd(DCE2_Smb2SsnData* ssd, const Smb2Hdr* smb_hdr, + const uint8_t* smb_data, const uint8_t* end, DCE2_Smb2TreeTracker* ttr) +{ + uint16_t structure_size = alignedNtohs((const uint16_t*)smb_data); + + if (structure_size == SMB2_ERROR_RESPONSE_STRUC_SIZE and Smb2Error(smb_hdr)) + { + dce2_smb_stats.v2_cls_err_resp++; + } + // Using structure size to decide whether it is response or request + else if (structure_size == SMB2_CLOSE_REQUEST_STRUC_SIZE) + { + SMB2_CHECK_HDR_ERROR( + smb_data, end, SMB2_CLOSE_REQUEST_STRUC_SIZE, + dce2_smb_stats.v2_cls_req_hdr_err) + + uint64_t fileId_persistent = alignedNtohq(&(((const Smb2CloseRequestHdr*)smb_data)->fileId_persistent)); + DCE2_Smb2FileTracker* ftracker = ttr->findFtracker(fileId_persistent); + if (!ftracker) + { + dce2_smb_stats.v2_cls_req_ftrkr_misng++; + return; + } + + if (!ftracker->file_size and ftracker->file_offset) + { + // If close command request comes just after create response, we dont have + // information to know the direction, hence below code was included. + FileDirection dir = DetectionEngine::get_current_packet()->is_from_client() ? + FILE_UPLOAD : FILE_DOWNLOAD; + + ftracker->file_size = ftracker->file_offset; + FileContext* file = get_smb_file_context(fileId_persistent); + if (file) + { + file->set_file_size(ftracker->file_size); + } + + // In case of upload/download of file with UNKNOWN size, we will not be able to + // detect malicious file during write request or read response. Once the close + // command request comes, we will go for file inspection and block an subsequent + // upload/download request for this file even with unknown size + DCE2_Smb2ProcessFileData(ssd, nullptr, 0, dir); + } + + if (ssd->ftracker_tcp and ssd->ftracker_tcp->file_id == fileId_persistent) + ssd->ftracker_tcp = nullptr; + + ttr->removeFtracker(fileId_persistent); + + } + else if (structure_size != SMB2_CLOSE_RESPONSE_STRUC_SIZE) + { + dce2_smb_stats.v2_cls_inv_str_sz++; + } +} + +//------------------------------------------------------------------------- +// Process set info request to update file size +//------------------------------------------------------------------------- +void DCE2_Smb2SetInfo(DCE2_Smb2SsnData*, const Smb2Hdr* smb_hdr, + const uint8_t* smb_data, const uint8_t* end, DCE2_Smb2TreeTracker* ttr) +{ + uint16_t structure_size = alignedNtohs((const uint16_t*)smb_data); + + // Using structure size to decide whether it is response or request + if (structure_size == SMB2_ERROR_RESPONSE_STRUC_SIZE and Smb2Error(smb_hdr)) + { + dce2_smb_stats.v2_stinf_err_resp++; + } + else if (structure_size == SMB2_SET_INFO_REQUEST_STRUC_SIZE) + { + SMB2_CHECK_HDR_ERROR( + smb_data, end, SMB2_SET_INFO_REQUEST_STRUC_SIZE, + dce2_smb_stats.v2_stinf_req_hdr_err) + + const Smb2SetInfoRequestHdr* smb_set_info_hdr = (const Smb2SetInfoRequestHdr*)smb_data; + const uint8_t* file_data = (const uint8_t*)smb_set_info_hdr + + SMB2_SET_INFO_REQUEST_STRUC_SIZE - 1; + + if (smb_set_info_hdr->file_info_class == SMB2_FILE_ENDOFFILE_INFO) + { + uint64_t file_size = alignedNtohq((const uint64_t*)file_data); + uint64_t fileId_persistent = alignedNtohq(&(smb_set_info_hdr->fileId_persistent)); + + DCE2_Smb2FileTracker* ftracker = ttr->findFtracker(fileId_persistent); + if (ftracker) + { + ftracker->file_size = file_size; + + FileContext* file = get_smb_file_context(fileId_persistent); + if (file) + { + file->set_file_size(ftracker->file_size); + } + } + else + dce2_smb_stats.v2_stinf_req_ftrkr_misng++; + } + else + dce2_smb_stats.v2_stinf_req_hdr_err++; + } + else if (structure_size != SMB2_SET_INFO_RESPONSE_STRUC_SIZE) + { + dce2_smb_stats.v2_stinf_inv_str_sz++; + } +} + +//------------------------------------------------------------------------- +// Process read request to create read request trackers to get file offset +//------------------------------------------------------------------------- +static void DCE2_Smb2ReadRequest(DCE2_Smb2SsnData* ssd, + const Smb2ReadRequestHdr* smb_read_hdr, const uint8_t*, DCE2_Smb2SessionTracker* str, + DCE2_Smb2TreeTracker* ttr, uint64_t message_id) +{ + DCE2_Smb2RequestTracker* readtracker = nullptr; + + uint64_t offset = alignedNtohq((const uint64_t*)(&(smb_read_hdr->offset))); + uint64_t fileId_persistent = alignedNtohq((const uint64_t*)(&(smb_read_hdr->fileId_persistent))); + + if (ssd->max_outstanding_requests > str->getTotalRequestsPending()) + { + readtracker = new DCE2_Smb2RequestTracker( + offset, fileId_persistent, nullptr, 0, nullptr); + ttr->insertDataRtracker(message_id, readtracker); + } + else + { + dce_alert(GID_DCE2, DCE2_SMB_MAX_REQS_EXCEEDED, (dce2CommonStats*)&dce2_smb_stats, + ssd->sd); + return; + } + + DCE2_Smb2FileTracker* ftracker = ttr->findFtracker(fileId_persistent); + if (!ftracker) // compounded create request + read request case + { + ftracker = new DCE2_Smb2FileTracker(fileId_persistent, nullptr, 0); + ttr->insertFtracker(fileId_persistent, ftracker); + } + + if (ftracker->file_size and (offset > ftracker->file_size)) + { + dce_alert(GID_DCE2, DCE2_SMB_INVALID_FILE_OFFSET, (dce2CommonStats*)&dce2_smb_stats, + ssd->sd); + } +} + +//------------------------------------------------------------------------- +// Process read response to send file data for inspection. read request +// trackers is cleaned after updating file offset in file tracker +//------------------------------------------------------------------------- +static void DCE2_Smb2ReadResponse(DCE2_Smb2SsnData* ssd, const Smb2Hdr* smb_hdr, + const Smb2ReadResponseHdr* smb_read_hdr, const uint8_t* end, DCE2_Smb2TreeTracker* ttr, + uint64_t message_id) +{ + const uint8_t* file_data = (const uint8_t*)smb_read_hdr + SMB2_READ_RESPONSE_STRUC_SIZE - 1; + int data_size = end - file_data; + uint16_t data_offset; + DCE2_Smb2RequestTracker* request; + + request = ttr->findDataRtracker(message_id); + if (!request) + { + dce2_smb_stats.v2_read_rtrkr_misng++; + return; + } + data_offset = alignedNtohs((const uint16_t*)(&(smb_read_hdr->data_offset))); + if (data_offset + (const uint8_t*)smb_hdr > end) + { + dce_alert(GID_DCE2, DCE2_SMB_BAD_OFF, (dce2CommonStats*)&dce2_smb_stats, ssd->sd); + } + + DCE2_Smb2FileTracker* ftracker = ttr->findFtracker(request->get_file_id()); + if ( ftracker ) // file tracker can never be NULL for read response + { + ftracker->file_offset = request->get_offset(); + ttr->removeDataRtracker(message_id); + ssd->ftracker_tcp = ftracker; + + DCE2_Smb2ProcessFileData(ssd, file_data, data_size, FILE_DOWNLOAD); + ftracker->file_offset += data_size; + + uint32_t total_data_length = alignedNtohl((const uint32_t*)&(smb_read_hdr->length)); + debug_logf(dce_smb_trace, nullptr, "smbv2 total_data=%d data_size=%d ssd=%p\n", total_data_length,data_size, + (void*)ssd); + if (total_data_length > (uint32_t)data_size) + { + ftracker->smb2_pdu_state = DCE2_SMB_PDU_STATE__RAW_DATA; + } + } +} + +//------------------------------------------------------------------------- +// Process read message +//------------------------------------------------------------------------- +void DCE2_Smb2Read(DCE2_Smb2SsnData* ssd, const Smb2Hdr* smb_hdr, + const uint8_t* smb_data, const uint8_t* end, DCE2_Smb2SessionTracker* str, + DCE2_Smb2TreeTracker* ttr, uint64_t mid) +{ + uint16_t structure_size = alignedNtohs((const uint16_t*)smb_data); + + if (Smb2Error(smb_hdr) and structure_size == SMB2_ERROR_RESPONSE_STRUC_SIZE) + { + DCE2_Smb2RequestTracker* rtr = ttr->findDataRtracker(mid); + if (rtr and rtr->get_file_id()) + { + if (ssd->ftracker_tcp->file_id == rtr->get_file_id()) + ssd->ftracker_tcp = NULL; + ttr->removeFtracker(rtr->get_file_id()); + } + ttr->removeDataRtracker(mid); + dce2_smb_stats.v2_read_err_resp++; + } + // Using structure size to decide whether it is response or request + else if (structure_size == SMB2_READ_REQUEST_STRUC_SIZE) + { + SMB2_CHECK_HDR_ERROR( + smb_data, end, SMB2_READ_REQUEST_STRUC_SIZE - 1, + dce2_smb_stats.v2_read_req_hdr_err) + DCE2_Smb2ReadRequest(ssd, (const Smb2ReadRequestHdr*)smb_data, end, str, ttr, mid); + } + else if (structure_size == SMB2_READ_RESPONSE_STRUC_SIZE) + { + SMB2_CHECK_HDR_ERROR( + smb_data, end, SMB2_READ_RESPONSE_STRUC_SIZE - 1, + dce2_smb_stats.v2_read_resp_hdr_err) + + DCE2_Smb2ReadResponse(ssd, smb_hdr, (const Smb2ReadResponseHdr*)smb_data, end, ttr, mid); + } + else + { + dce2_smb_stats.v2_read_inv_str_sz++; + } +} + +//------------------------------------------------------------------------- +// Process write request to create write trackers (to enforce credits limit) +// and to send file data for inspection. +//------------------------------------------------------------------------- +static void DCE2_Smb2WriteRequest(DCE2_Smb2SsnData* ssd, const Smb2Hdr* smb_hdr, + const Smb2WriteRequestHdr* smb_write_hdr, const uint8_t* end, DCE2_Smb2SessionTracker* str, + DCE2_Smb2TreeTracker* ttr, uint64_t mid) +{ + const uint8_t* file_data = (const uint8_t*)smb_write_hdr + SMB2_WRITE_REQUEST_STRUC_SIZE - 1; + int data_size = end - file_data; + uint64_t fileId_persistent, offset; + uint16_t data_offset; + DCE2_Smb2RequestTracker* writetracker = nullptr; + + fileId_persistent = alignedNtohq((const uint64_t*)(&(smb_write_hdr->fileId_persistent))); + + if (ssd->max_outstanding_requests > str->getTotalRequestsPending()) + { + writetracker = new DCE2_Smb2RequestTracker( + 0, fileId_persistent, nullptr, 0, nullptr); + ttr->insertDataRtracker(mid, writetracker); + } + else + { + dce_alert(GID_DCE2, DCE2_SMB_MAX_REQS_EXCEEDED, (dce2CommonStats*)&dce2_smb_stats, + ssd->sd); + return; + } + + data_offset = alignedNtohs((const uint16_t*)(&(smb_write_hdr->data_offset))); + if (data_offset + (const uint8_t*)smb_hdr > end) + { + dce_alert(GID_DCE2, DCE2_SMB_BAD_OFF, (dce2CommonStats*)&dce2_smb_stats, ssd->sd); + } + + offset = alignedNtohq((const uint64_t*)(&(smb_write_hdr->offset))); + DCE2_Smb2FileTracker* ftracker = ttr->findFtracker(fileId_persistent); + if (ftracker) // file tracker can not be NULL here + { + if (ftracker->file_size and (offset > ftracker->file_size)) + { + dce_alert(GID_DCE2, DCE2_SMB_INVALID_FILE_OFFSET, (dce2CommonStats*)&dce2_smb_stats, + ssd->sd); + } + ftracker->file_offset = offset; + ssd->ftracker_tcp = ftracker; + DCE2_Smb2ProcessFileData(ssd, file_data, data_size, FILE_UPLOAD); + ftracker->file_offset += data_size; + uint32_t total_data_length = alignedNtohl((const uint32_t*)&(smb_write_hdr->length)); + debug_logf(dce_smb_trace, nullptr, "smbv2 total_data=%d data_size=%d\n",total_data_length,data_size); + if (total_data_length > (uint32_t)data_size) + { + ftracker->smb2_pdu_state = DCE2_SMB_PDU_STATE__RAW_DATA; + } + } +} + +//------------------------------------------------------------------------- +// Process write message +//------------------------------------------------------------------------- +void DCE2_Smb2Write(DCE2_Smb2SsnData* ssd, const Smb2Hdr* smb_hdr, + const uint8_t* smb_data, const uint8_t* end, DCE2_Smb2SessionTracker* str, + DCE2_Smb2TreeTracker* ttr, uint64_t mid) +{ + uint16_t structure_size = alignedNtohs((const uint16_t*)smb_data); + + if (structure_size == SMB2_ERROR_RESPONSE_STRUC_SIZE and Smb2Error(smb_hdr)) + { + DCE2_Smb2RequestTracker* wtr = ttr->findDataRtracker(mid); + if (wtr and wtr->get_file_id()) + { + if (ssd->ftracker_tcp->file_id == wtr->get_file_id()) + ssd->ftracker_tcp = NULL; + ttr->removeFtracker(wtr->get_file_id()); + } + ttr->removeDataRtracker(mid); + dce2_smb_stats.v2_wrt_err_resp++; + } + // Using structure size to decide whether it is response or request + else if (structure_size == SMB2_WRITE_REQUEST_STRUC_SIZE) + { + SMB2_CHECK_HDR_ERROR( + smb_data, end, SMB2_WRITE_REQUEST_STRUC_SIZE - 1, + dce2_smb_stats.v2_wrt_req_hdr_err) + DCE2_Smb2WriteRequest(ssd, smb_hdr, (const Smb2WriteRequestHdr*)smb_data, end, str, ttr, mid); + } + else if (structure_size == SMB2_WRITE_RESPONSE_STRUC_SIZE) + { + ttr->removeDataRtracker(mid); + } + else + { + dce2_smb_stats.v2_wrt_inv_str_sz++; + } +} + +//------------------------------------------------------------------------- +// Process logoff to cleanup session tracker and their corresponding tree +// trackers and their corresponding file trackers +//------------------------------------------------------------------------- +void DCE2_Smb2Logoff(DCE2_Smb2SsnData* ssd, const uint8_t* smb_data, + const uint64_t sid) +{ + if (alignedNtohs((const uint16_t*)smb_data) == SMB2_LOGOFF_REQUEST_STRUC_SIZE) + { + DCE2_Smb2RemoveSidInSsd(ssd, sid); + } + else + { + dce2_smb_stats.v2_logoff_inv_str_sz++; + } +} diff --git a/src/service_inspectors/dce_rpc/dce_smb2_commands.h b/src/service_inspectors/dce_rpc/dce_smb2_commands.h new file mode 100644 index 000000000..f67553870 --- /dev/null +++ b/src/service_inspectors/dce_rpc/dce_smb2_commands.h @@ -0,0 +1,65 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2016-2019 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_smb2_commands.h author Bhargava Jandhyala +// based on work by Todd Wease + +#ifndef DCE_SMB2_COMMANDS_H +#define DCE_SMB2_COMMANDS_H + +#include "dce_smb_module.h" +#include "dce_smb_utils.h" +#include "dce_smb2_utils.h" +#include "detection/detection_util.h" +#include "file_api/file_flows.h" +#include "file_api/file_service.h" + +void DCE2_Smb2Setup(DCE2_Smb2SsnData*, const Smb2Hdr*, + const uint64_t sid, const uint8_t* smb_data, const uint8_t* end); + +void DCE2_Smb2TreeConnect(DCE2_Smb2SsnData*, const Smb2Hdr*, + const uint8_t* smb_data, const uint8_t* end, DCE2_Smb2SessionTracker* str, uint32_t tid); + +void DCE2_Smb2TreeDisconnect(DCE2_Smb2SsnData*, const uint8_t* smb_data, + const uint8_t* end, DCE2_Smb2SessionTracker* str, uint32_t tid); + +void DCE2_Smb2Create(DCE2_Smb2SsnData*, const Smb2Hdr*, + const uint8_t* smb_data, const uint8_t* end, uint64_t mid, uint64_t sid, uint32_t tid); + +void DCE2_Smb2Read(DCE2_Smb2SsnData*, const Smb2Hdr*, + const uint8_t* smb_data, const uint8_t* end, DCE2_Smb2SessionTracker* str, + DCE2_Smb2TreeTracker* ttr, uint64_t mid); + +void DCE2_Smb2Write(DCE2_Smb2SsnData*, const Smb2Hdr*, + const uint8_t* smb_data, const uint8_t* end, DCE2_Smb2SessionTracker* str, + DCE2_Smb2TreeTracker* ttr, uint64_t mid); + +void DCE2_Smb2SetInfo(DCE2_Smb2SsnData*, const Smb2Hdr*, + const uint8_t* smb_data, const uint8_t* end, DCE2_Smb2TreeTracker* ttr); + +void DCE2_Smb2ProcessFileData(DCE2_Smb2SsnData*, const uint8_t* file_data, + uint32_t data_size, FileDirection dir); + +void DCE2_Smb2CloseCmd(DCE2_Smb2SsnData*, const Smb2Hdr*, + const uint8_t* smb_data, const uint8_t* end, DCE2_Smb2TreeTracker* ttr); + +void DCE2_Smb2Logoff(DCE2_Smb2SsnData*, const uint8_t* smb_data, + const uint64_t sid); + +#endif + diff --git a/src/service_inspectors/dce_rpc/dce_smb2_utils.cc b/src/service_inspectors/dce_rpc/dce_smb2_utils.cc new file mode 100644 index 000000000..995ba9599 --- /dev/null +++ b/src/service_inspectors/dce_rpc/dce_smb2_utils.cc @@ -0,0 +1,86 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2015-2019 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. +//-------------------------------------------------------------------------- + +// SMB2 utils processing +// dce_smb2_utils.cc author Bhargava Jandhyala + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "dce_smb_module.h" +#include "dce_smb_utils.h" +#include "dce_smb2_utils.h" +#include "detection/detection_util.h" +#include "main/snort_debug.h" + +using namespace snort; + +size_t session_cache_size; +THREAD_LOCAL SmbSessionCache* smb2_session_cache; + +DCE2_Smb2SessionTracker* DCE2_Smb2FindElseCreateSid(DCE2_Smb2SsnData* ssd, const + uint64_t sid) +{ + // Local MAP search + DCE2_Smb2SessionTracker* stracker = DCE2_Smb2FindSidInSsd(ssd, sid); + + if (!stracker) + { + // Global Hash Search + bool entry_created = false; + stracker = DCE2_SmbSessionCacheFindElseCreate(sid, &entry_created); + assert(stracker); + if (entry_created) + { + stracker->set_session_id(sid); + } + DCE2_Smb2InsertSidInSsd(ssd, sid, stracker); + } + + return stracker; +} + +DCE2_Smb2TreeTracker* DCE2_Smb2InsertTid(DCE2_Smb2SsnData* ssd, const uint32_t tid, uint8_t share_type, + DCE2_Smb2SessionTracker* str) +{ + if (share_type == SMB2_SHARE_TYPE_DISK and + ssd->max_file_depth == -1 and DCE2_ScSmbFileDepth((dce2SmbProtoConf*)ssd->sd.config) == -1) + { + debug_logf(dce_smb_trace, nullptr, "Not inserting TID (%u) because it's " + "not IPC and not inspecting normal file data.\n", tid); + return nullptr; + } + + DCE2_Smb2TreeTracker* ttracker = new DCE2_Smb2TreeTracker(tid, share_type); + str->insertTtracker(tid, ttracker); + return ttracker; +} + +DCE2_Smb2TreeTracker* DCE2_Smb2FindElseCreateTid(DCE2_Smb2SsnData* ssd, const uint32_t tid, + uint8_t share_type, DCE2_Smb2SessionTracker* str) +{ + DCE2_Smb2TreeTracker* ttr = str->findTtracker(tid); + if (!ttr) + { + ttr = DCE2_Smb2InsertTid(ssd, tid, share_type, str); + } + + return ttr; +} + diff --git a/src/service_inspectors/dce_rpc/dce_smb2_utils.h b/src/service_inspectors/dce_rpc/dce_smb2_utils.h new file mode 100644 index 000000000..f8fe5a57d --- /dev/null +++ b/src/service_inspectors/dce_rpc/dce_smb2_utils.h @@ -0,0 +1,119 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2016-2019 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_smb2_utils.h author Bhargava Jandhyala +// based on work by Todd Wease + +#ifndef DCE_SMB2_UTILS_H +#define DCE_SMB2_UTILS_H + +#include "dce_smb.h" +#include "dce_smb2.h" +#include "file_api/file_flows.h" +#include "hash/lru_cache_shared.h" + +#define SMB2_SID_HASH(sid) std::hash()(sid) + +template +class SmbSessionCache_map : public LruCacheShared +{ +public: + SmbSessionCache_map() = delete; + SmbSessionCache_map(const SmbSessionCache_map& arg) = delete; + SmbSessionCache_map& operator=(const SmbSessionCache_map& arg) = delete; + SmbSessionCache_map(const size_t initial_size) : LruCacheShared(initial_size) {} + virtual ~SmbSessionCache_map() {} +}; + +typedef SmbSessionCache_map > SmbSessionCache; + +extern THREAD_LOCAL SmbSessionCache* smb2_session_cache; +extern size_t session_cache_size; + +// SMB2 Session cache manipulation functions +inline void DCE2_SmbSessionCacheInit(const size_t cache_size) +{ + smb2_session_cache = new SmbSessionCache(cache_size); +} + +inline DCE2_Smb2SessionTracker* DCE2_SmbSessionCacheFind(uint64_t sid) +{ + return (smb2_session_cache->find(SMB2_SID_HASH(sid))).get(); +} + +inline DCE2_Smb2SessionTracker* DCE2_SmbSessionCacheFindElseCreate(uint64_t sid, + bool* entry_created) +{ + return (smb2_session_cache->find_else_create(SMB2_SID_HASH(sid), entry_created)).get(); +} + +inline bool DCE2_SmbSessionCacheRemove(uint64_t sid) +{ + return smb2_session_cache->remove(SMB2_SID_HASH(sid)); +} + +// SMB2 functions for fetching sid, tid, request type and so on. +inline uint64_t Smb2Sid(const Smb2Hdr* hdr) +{ + return snort::alignedNtohq(&(((const Smb2SyncHdr*)hdr)->session_id)); +} + +inline uint32_t Smb2Tid(const Smb2Hdr* hdr) +{ + return snort::alignedNtohl(&(((const Smb2SyncHdr*)hdr)->tree_id)); +} + +inline uint64_t Smb2Mid(const Smb2Hdr* hdr) +{ + return snort::alignedNtohq(&(((const Smb2SyncHdr*)hdr)->message_id)); +} + +inline bool Smb2Error(const Smb2Hdr* hdr) +{ + return (SMB_NT_STATUS_SEVERITY__ERROR == (uint8_t)(hdr->status >> 30)); +} + +inline DCE2_Smb2SessionTracker* DCE2_Smb2FindSidInSsd(DCE2_Smb2SsnData* ssd, const + uint64_t sid) +{ + return (DCE2_Smb2SessionTracker*)(ssd->session_trackers.Find(sid)); +} + +inline void DCE2_Smb2InsertSidInSsd(DCE2_Smb2SsnData* ssd, const uint64_t sid, + DCE2_Smb2SessionTracker* stracker) +{ + ssd->session_trackers.Insert(sid, stracker); +} + +inline void DCE2_Smb2RemoveSidInSsd(DCE2_Smb2SsnData* ssd, const uint64_t sid) +{ + ssd->session_trackers.Remove(sid); +} + +DCE2_Smb2TreeTracker* DCE2_Smb2InsertTid(DCE2_Smb2SsnData*, const uint32_t tid, uint8_t share_type, + DCE2_Smb2SessionTracker*); + +DCE2_Smb2TreeTracker* DCE2_Smb2FindElseCreateTid(DCE2_Smb2SsnData*, const uint32_t tid, + uint8_t share_type, DCE2_Smb2SessionTracker*); + +DCE2_Smb2SessionTracker* DCE2_Smb2FindElseCreateSid(DCE2_Smb2SsnData*, const uint64_t sid); + +DCE2_Ret DCE2_Smb2InitData(DCE2_Smb2SsnData*); + +#endif + diff --git a/src/service_inspectors/dce_rpc/dce_smb_module.cc b/src/service_inspectors/dce_rpc/dce_smb_module.cc index 2c8dc15f5..59785958c 100644 --- a/src/service_inspectors/dce_rpc/dce_smb_module.cc +++ b/src/service_inspectors/dce_rpc/dce_smb_module.cc @@ -83,14 +83,99 @@ static const PegInfo dce2_smb_pegs[] = { CountType::SUM, "smb_server_segs_reassembled", "total smb server segments reassembled" }, { CountType::SUM, "max_outstanding_requests", "total smb maximum outstanding requests" }, { CountType::SUM, "files_processed", "total smb files processed" }, - { CountType::SUM, "smbv2_create", "total number of SMBv2 create packets seen" }, - { CountType::SUM, "smbv2_write", "total number of SMBv2 write packets seen" }, - { CountType::SUM, "smbv2_read", "total number of SMBv2 read packets seen" }, - { CountType::SUM, "smbv2_set_info", "total number of SMBv2 set info packets seen" }, - { CountType::SUM, "smbv2_tree_connect", "total number of SMBv2 tree connect packets seen" }, - { CountType::SUM, "smbv2_tree_disconnect", + { CountType::SUM, "v2_setup", "total number of SMBv2 setup packets seen" }, + { CountType::SUM, "v2_setup_err_resp", "total number of SMBv2 setup error response packets seen" }, + { CountType::SUM, "v2_setup_inv_str_sz", + "total number of SMBv2 setup packets seen with invalid structure size" }, + { CountType::SUM, "v2_setup_resp_hdr_err", + "total number of SMBv2 setup response packets ignored due to corrupted header" }, + { CountType::SUM, "v2_tree_cnct", "total number of SMBv2 tree connect packets seen" }, + { CountType::SUM, "v2_tree_cnct_err_resp", + "total number of SMBv2 tree connect error response packets seen" }, + { CountType::SUM, "v2_tree_cnct_ignored", + "total number of SMBv2 setup response packets ignored due to failure in creating tree tracker" }, + { CountType::SUM, "v2_tree_cnct_inv_str_sz", + "total number of SMBv2 tree connect packets seen with invalid structure size" }, + { CountType::SUM, "v2_tree_cnct_resp_hdr_err", + "total number of SMBv2 tree connect response packets ignored due to corrupted header" }, + { CountType::SUM, "v2_crt", "total number of SMBv2 create packets seen" }, + { CountType::SUM, "v2_crt_err_resp", "total number of SMBv2 create error response packets seen" }, + { CountType::SUM, "v2_crt_inv_file_data", + "total number of SMBv2 create request packets ignored due to error in getting file name" }, + { CountType::SUM, "v2_crt_inv_str_sz", + "total number of SMBv2 create packets seen with invalid structure size" }, + { CountType::SUM, "v2_crt_resp_hdr_err", + "total number of SMBv2 create response packets ignored due to corrupted header" }, + { CountType::SUM, "v2_crt_req_hdr_err", + "total number of SMBv2 create request packets ignored due to corrupted header" }, + { CountType::SUM, "v2_crt_rtrkr_misng", + "total number of SMBv2 create response packets ignored due to missing create request tracker" }, + { CountType::SUM, "v2_crt_req_ipc", + "total number of SMBv2 create request packets ignored as share type is IPC" }, + { CountType::SUM, "v2_crt_tree_trkr_misng", + "total number of SMBv2 create response packets ignored due to missing tree tracker" }, + { CountType::SUM, "v2_wrt", "total number of SMBv2 write packets seen" }, + { CountType::SUM, "v2_wrt_err_resp", "total number of SMBv2 write error response packets seen" }, + { CountType::SUM, "v2_wrt_ignored", + "total number of SMBv2 write packets ignored due to missing trackers or invalid share type" }, + { CountType::SUM, "v2_wrt_inv_str_sz", + "total number of SMBv2 write packets seen with invalid structure size" }, + { CountType::SUM, "v2_wrt_req_hdr_err", + "total number of SMBv2 write request packets ignored due to corrupted header" }, + { CountType::SUM, "v2_read", "total number of SMBv2 read packets seen" }, + { CountType::SUM, "v2_read_err_resp", "total number of SMBv2 read error response packets seen" }, + { CountType::SUM, "v2_read_ignored", + "total number of SMBv2 write packets ignored due to missing trackers or invalid share type" }, + { CountType::SUM, "v2_read_inv_str_sz", + "total number of SMBv2 read packets seen with invalid structure size" }, + { CountType::SUM, "v2_read_rtrkr_misng", + "total number of SMBv2 read response packets ignored due to missing read request tracker" }, + { CountType::SUM, "v2_read_resp_hdr_err", + "total number of SMBv2 read response packets ignored due to corrupted header" }, + { CountType::SUM, "v2_read_req_hdr_err", + "total number of SMBv2 read request packets ignored due to corrupted header" }, + { CountType::SUM, "v2_stinf", "total number of SMBv2 set info packets seen" }, + { CountType::SUM, "v2_stinf_err_resp", "total number of SMBv2 set info error response packets seen" }, + { CountType::SUM, "v2_stinf_ignored", + "total number of SMBv2 set info packets ignored due to missing trackers or invalid share type" }, + { CountType::SUM, "v2_stinf_inv_str_sz", + "total number of SMBv2 set info packets seen with invalid structure size" }, + { CountType::SUM, "v2_stinf_req_ftrkr_misng", + "total number of SMBv2 set info request packets ignored due to missing file tracker" }, + { CountType::SUM, "v2_stinf_req_hdr_err", + "total number of SMBv2 set info request packets ignored due to corrupted header" }, + { CountType::SUM, "v2_cls", "total number of SMBv2 close packets seen" }, + { CountType::SUM, "v2_cls_err_resp", "total number of SMBv2 close error response packets seen" }, + { CountType::SUM, "v2_cls_ignored", + "total number of SMBv2 close packets ignored due to missing trackers or invalid share type" }, + { CountType::SUM, "v2_cls_inv_str_sz", + "total number of SMBv2 close packets seen with invalid structure size" }, + { CountType::SUM, "v2_cls_req_ftrkr_misng", + "total number of SMBv2 close request packets ignored due to missing file tracker" }, + { CountType::SUM, "v2_cls_req_hdr_err", + "total number of SMBv2 close request packets ignored due to corrupted header" }, + { CountType::SUM, "v2_tree_discn", "total number of SMBv2 tree disconnect packets seen" }, - { CountType::SUM, "smbv2_close", "total number of SMBv2 close packets seen" }, + { CountType::SUM, "v2_tree_discn_ignored", + "total number of SMBv2 tree disconnect packets ignored due to missing trackers or invalid share type" }, + { CountType::SUM, "v2_tree_discn_inv_str_sz", + "total number of SMBv2 tree disconnect packets seen with invalid structure size" }, + { CountType::SUM, "v2_tree_discn_req_hdr_err", + "total number of SMBv2 tree disconnect request packets ignored due to corrupted header" }, + { CountType::SUM, "v2_logoff", "total number of SMBv2 logoff" }, + { CountType::SUM, "v2_logoff_inv_str_sz", + "total number of SMBv2 logoff packets seen with invalid structure size" }, + { CountType::SUM, "v2_hdr_err", "total number of SMBv2 packets seen with corrupted hdr" }, + { CountType::SUM, "v2_bad_next_cmd_offset", + "total number of SMBv2 packets seen with invalid next command offset" }, + { CountType::SUM, "v2_extra_file_data_err", + "total number of SMBv2 packets seen with where file data beyond file size is observed" }, + { CountType::SUM, "v2_inv_file_ctx_err", + "total number of times null file context are seen resulting in not being able to set file size" }, + { CountType::SUM, "v2_msgs_uninspected", + "total number of SMBv2 packets seen where command is not being inspected" }, + { CountType::SUM, "v2_cmpnd_req_lt_crossed", + "total number of SMBv2 packets seen where compound requests exceed the smb_max_compound limit" }, { CountType::NOW, "concurrent_sessions", "total concurrent sessions" }, { CountType::MAX, "max_concurrent_sessions", "maximum concurrent sessions" }, { CountType::END, nullptr, nullptr } @@ -142,6 +227,12 @@ static const Parameter s_params[] = { "smb_legacy_mode", Parameter::PT_BOOL, nullptr, "false", "inspect only SMBv1" }, + { "smb_max_credit", Parameter::PT_INT, "1:65536", "8192", + "Maximum number of outstanding request" }, + + { "memcap", Parameter::PT_INT, "512:maxSZ", "8388608", + "Memory utilization limit on smb" }, + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } }; @@ -415,6 +506,12 @@ bool Dce2SmbModule::set(const char*, Value& v, SnortConfig*) else if ( v.is("smb_legacy_mode")) config.legacy_mode = v.get_bool(); + else if ( v.is("smb_max_credit") ) + config.smb_max_credit = v.get_uint16(); + + else if ( v.is("memcap") ) + config.memcap = v.get_size(); + return true; } @@ -438,5 +535,7 @@ void print_dce2_smb_conf(const dce2SmbProtoConf& config) ConfigLogger::log_list("smb_invalid_shares", get_shares(config.smb_invalid_shares).c_str()); ConfigLogger::log_flag("smb_legacy_mode", config.legacy_mode); + ConfigLogger::log_limit("smb_max_credit", config.smb_max_credit, 0, 1); + } diff --git a/src/service_inspectors/dce_rpc/dce_smb_module.h b/src/service_inspectors/dce_rpc/dce_smb_module.h index 9dbacd0d4..e863032db 100644 --- a/src/service_inspectors/dce_rpc/dce_smb_module.h +++ b/src/service_inspectors/dce_rpc/dce_smb_module.h @@ -62,6 +62,8 @@ struct dce2SmbProtoConf int16_t smb_file_depth; DCE2_List* smb_invalid_shares; bool legacy_mode; + uint16_t smb_max_credit; + size_t memcap; }; class Dce2SmbModule : public snort::Module @@ -112,6 +114,27 @@ inline DCE2_List* DCE2_ScSmbInvalidShares(const dce2SmbProtoConf* sc) return sc->smb_invalid_shares; } +inline uint16_t DCE2_ScSmbMaxCredit(const dce2SmbProtoConf* sc) +{ + if (sc == nullptr) + return 8192; + return sc->smb_max_credit; +} + +inline size_t DCE2_ScSmbMemcap(const dce2SmbProtoConf* sc) +{ + if (sc == nullptr) + return 8388608; + return sc->memcap; +} + +inline uint16_t DCE2_ScSmbMaxCompound(const dce2SmbProtoConf* sc) +{ + if (sc == nullptr) + return 3; + return sc->smb_max_compound; +} + inline bool DCE2_GcSmbFingerprintClient(const dce2SmbProtoConf* sc) { if (sc == nullptr) diff --git a/src/service_inspectors/dce_rpc/dce_smb_paf.cc b/src/service_inspectors/dce_rpc/dce_smb_paf.cc index 730458de5..f48057fe5 100644 --- a/src/service_inspectors/dce_rpc/dce_smb_paf.cc +++ b/src/service_inspectors/dce_rpc/dce_smb_paf.cc @@ -114,9 +114,9 @@ static StreamSplitter::Status dce2_smb_paf(DCE2_PafSmbData* ss, Flow* flow, cons const SmbNtHdr* nt_hdr = nullptr; uint32_t nb_len = 0; - DCE2_SmbSsnData* sd = get_dce2_smb_session_data(flow); + DCE2_SsnData* sd = get_dce2_session_data(flow); - if ( dce2_paf_abort((DCE2_SsnData*)sd) ) + if ( dce2_paf_abort(sd) ) { return StreamSplitter::ABORT; } diff --git a/src/service_inspectors/dce_rpc/dev_notes.txt b/src/service_inspectors/dce_rpc/dev_notes.txt index 5053c7df1..18c40a787 100644 --- a/src/service_inspectors/dce_rpc/dev_notes.txt +++ b/src/service_inspectors/dce_rpc/dev_notes.txt @@ -24,3 +24,8 @@ The http_proxy and http_server functionality is implemented as two inspectors. These inspectors only serve to locate the 'tunnel' setup content. If/when the setup content is located, the session is transfered to the DCE TCP inspector. + +The SMB inspector supports version 1 and version 2 of the protocol. +It processes relevant messages and inspects file transferred over this transport. +For SMBv2, it supports TCP as transport and it also supports +Multiple File transfer on single SMB2 connection. diff --git a/src/service_inspectors/dce_rpc/smb_message.cc b/src/service_inspectors/dce_rpc/smb_message.cc index e635e8b80..e2767699f 100644 --- a/src/service_inspectors/dce_rpc/smb_message.cc +++ b/src/service_inspectors/dce_rpc/smb_message.cc @@ -22,22 +22,19 @@ #include "config.h" #endif -#include "dce_smb.h" +#include "smb_message.h" +#include "dce_smb_commands.h" +#include "dce_smb_module.h" +#include "dce_smb_paf.h" +#include "dce_smb_transaction.h" +#include "dce_smb2_utils.h" #include "detection/detect.h" #include "file_api/file_service.h" #include "main/snort_debug.h" +#include "packet_io/active.h" #include "protocols/packet.h" #include "utils/util.h" -#include "packet_io/active.h" - -#include "dce_smb.h" -#include "dce_smb_commands.h" -#include "dce_smb_module.h" -#include "dce_smb_paf.h" -#include "dce_smb_transaction.h" -#include "dce_smb_utils.h" -#include "dce_smb2.h" using namespace snort; @@ -1243,7 +1240,7 @@ static void DCE2_SmbProcessRawData(DCE2_SmbSsnData* ssd, const uint8_t* nb_ptr, } } -static void DCE2_SmbDataFree(DCE2_SmbSsnData* ssd) +void DCE2_SmbDataFree(DCE2_SmbSsnData* ssd) { if (ssd == nullptr) return; @@ -1291,12 +1288,6 @@ static void DCE2_SmbDataFree(DCE2_SmbSsnData* ssd) DCE2_BufferDestroy(ssd->srv_seg); ssd->srv_seg = nullptr; } - - if (ssd->smb2_requests != nullptr) - { - DCE2_Smb2CleanRequests(ssd->smb2_requests); - ssd->smb2_requests = nullptr; - } } Dce2SmbFlowData::Dce2SmbFlowData() : FlowData(inspector_id) @@ -1304,32 +1295,37 @@ Dce2SmbFlowData::Dce2SmbFlowData() : FlowData(inspector_id) dce2_smb_stats.concurrent_sessions++; if (dce2_smb_stats.max_concurrent_sessions < dce2_smb_stats.concurrent_sessions) dce2_smb_stats.max_concurrent_sessions = dce2_smb_stats.concurrent_sessions; + smb_version = DCE2_SMB_VERSION_NULL; + dce2_smb_session_data = nullptr; } Dce2SmbFlowData::~Dce2SmbFlowData() { - DCE2_SmbDataFree(&dce2_smb_session); + if (DCE2_SMB_VERSION_1 == smb_version) + { + DCE2_SmbDataFree((DCE2_SmbSsnData*)dce2_smb_session_data); + delete (DCE2_SmbSsnData*)dce2_smb_session_data; + } + else + { + delete (DCE2_Smb2SsnData*)dce2_smb_session_data; + } assert(dce2_smb_stats.concurrent_sessions > 0); dce2_smb_stats.concurrent_sessions--; } unsigned Dce2SmbFlowData::inspector_id = 0; -DCE2_SmbSsnData* get_dce2_smb_session_data(Flow* flow) -{ - Dce2SmbFlowData* fd = (Dce2SmbFlowData*)flow->get_flow_data(Dce2SmbFlowData::inspector_id); - return fd ? &fd->dce2_smb_session : nullptr; -} - -static DCE2_SmbSsnData* set_new_dce2_smb_session(Packet* p) +static inline DCE2_SmbSsnData* set_new_dce2_smb_session(Packet* p) { Dce2SmbFlowData* fd = new Dce2SmbFlowData; - memset(&fd->dce2_smb_session,0,sizeof(DCE2_SmbSsnData)); + fd->smb_version = DCE2_SMB_VERSION_1; + fd->dce2_smb_session_data = new DCE2_SmbSsnData(); p->flow->set_flow_data(fd); - return(&fd->dce2_smb_session); + return (DCE2_SmbSsnData*)(fd->dce2_smb_session_data); } -static DCE2_SmbSsnData* dce2_create_new_smb_session(Packet* p, dce2SmbProtoConf* config) +DCE2_SmbSsnData* dce2_create_new_smb_session(Packet* p, dce2SmbProtoConf* config) { DCE2_SmbSsnData* dce2_smb_sess = set_new_dce2_smb_session(p); if ( dce2_smb_sess ) @@ -1456,11 +1452,11 @@ static DCE2_Ret DCE2_NbssHdrChecks(DCE2_SmbSsnData* ssd, const NbssHdr* nb_hdr) } // This is the main entry point for SMB1 processing. -static void DCE2_Smb1Process(DCE2_SmbSsnData* ssd) +void DCE2_Smb1Process(DCE2_SmbSsnData* ssd) { dce2_smb_stats.smb_pkts++; - const Packet* p = DetectionEngine::get_current_packet(); + Packet* p = DetectionEngine::get_current_packet(); const uint8_t* data_ptr = p->data; uint16_t data_len = p->dsize; DCE2_Buffer** seg_buf = DCE2_SmbGetSegBuffer(ssd); @@ -1615,12 +1611,19 @@ static void DCE2_Smb1Process(DCE2_SmbSsnData* ssd) if (SmbId(smb_hdr) == DCE2_SMB2_ID) { - ssd->sd.flags |= DCE2_SSN_FLAG__SMB2; if (!DCE2_GcIsLegacyMode((dce2SmbProtoConf*)ssd->sd.config)) { - DCE2_Smb2InitFileTracker(&(ssd->ftracker), false, 0); - DCE2_Smb2Process(ssd); + // Upgrade connection to SMBv2 + dce2SmbProtoConf* config = (dce2SmbProtoConf*)ssd->sd.config; + Dce2SmbFlowData* fd = (Dce2SmbFlowData*)p->flow->get_flow_data(Dce2SmbFlowData::inspector_id); + p->flow->free_flow_data(fd); + DCE2_Smb2SsnData* dce2_smb2_sess = dce2_create_new_smb2_session(p, config); + DCE2_Smb2Process(dce2_smb2_sess); + if(!dce2_detected) + DCE2_Detect(&dce2_smb2_sess->sd); } + else + ssd->sd.flags |= DCE2_SSN_FLAG__SMB2; return; } @@ -2542,51 +2545,48 @@ void DCE2_SmbInitGlobals() } } -DCE2_SmbSsnData* dce2_handle_smb_session(Packet* p, dce2SmbProtoConf* config) -{ - DCE2_SmbSsnData* dce2_smb_sess = get_dce2_smb_session_data(p->flow); +/* smb2 Functions */ - if (dce2_smb_sess == nullptr) - { - dce2_smb_sess = dce2_create_new_smb_session(p, config); - } - - return dce2_smb_sess; +static inline DCE2_Smb2SsnData* set_new_dce2_smb2_session(Packet* p) +{ + Dce2SmbFlowData* fd = new Dce2SmbFlowData; + fd->smb_version = DCE2_SMB_VERSION_2; + fd->dce2_smb_session_data = new DCE2_Smb2SsnData(); + DCE2_Smb2InitData((DCE2_Smb2SsnData*)fd->dce2_smb_session_data); + p->flow->set_flow_data(fd); + return((DCE2_Smb2SsnData*)fd->dce2_smb_session_data); } -// This is the main entry point for SMB processing -void DCE2_SmbProcess(DCE2_SmbSsnData* ssd) +DCE2_Smb2SsnData* dce2_create_new_smb2_session(Packet* p, dce2SmbProtoConf* config) { - if (DCE2_GcIsLegacyMode((dce2SmbProtoConf*)ssd->sd.config)) + DCE2_Smb2SsnData* dce2_smb2_sess = set_new_dce2_smb2_session(p); + if ( dce2_smb2_sess ) { - DCE2_Smb1Process(ssd); - return; - } + dce2_smb2_sess->sd.flags |= DCE2_SSN_FLAG__SMB2; + dce2_smb2_sess->dialect_index = DCE2_SENTINEL; - Packet* p = DetectionEngine::get_current_packet(); - DCE2_SmbVersion smb_version = DCE2_Smb2Version(p); - if (smb_version == DCE2_SMB_VERISON_1) - { - if ((ssd->sd.flags & DCE2_SSN_FLAG__SMB2)) - { - ssd->sd.flags &= ~DCE2_SSN_FLAG__SMB2; - DCE2_SmbCleanFileTracker(&(ssd->ftracker)); - ssd->ftracker.is_smb2 = false; - } - } - else if (smb_version == DCE2_SMB_VERISON_2) - { - if (!(ssd->sd.flags & DCE2_SSN_FLAG__SMB2)) - { - DCE2_SmbCleanFileTracker(&(ssd->ftracker)); - DCE2_Smb2InitFileTracker(&(ssd->ftracker), false, 0); - ssd->sd.flags |= DCE2_SSN_FLAG__SMB2; - } + DCE2_ResetRopts(&dce2_smb2_sess->sd, p); + + dce2_smb_stats.smb_sessions++; + + dce2_smb2_sess->sd.trans = DCE2_TRANS_TYPE__SMB; + dce2_smb2_sess->sd.server_policy = config->common.policy; + dce2_smb2_sess->sd.client_policy = DCE2_POLICY__WINXP; + dce2_smb2_sess->sd.config = (void*)config; + dce2_smb2_sess->max_outstanding_requests = DCE2_ScSmbMaxCredit(config); } - if (ssd->sd.flags & DCE2_SSN_FLAG__SMB2) - DCE2_Smb2Process(ssd); - else - DCE2_Smb1Process(ssd); + return dce2_smb2_sess; +} + +DCE2_SsnData* get_dce2_session_data(snort::Flow* flow) +{ + Dce2SmbFlowData* fd = (Dce2SmbFlowData*)flow->get_flow_data(Dce2SmbFlowData::inspector_id); + if (fd and fd->dce2_smb_session_data) + return (fd->smb_version == DCE2_SMB_VERSION_1) ? + (DCE2_SsnData*)(&((DCE2_SmbSsnData*)fd->dce2_smb_session_data)->sd) : + (DCE2_SsnData*)(&((DCE2_Smb2SsnData*)fd->dce2_smb_session_data)->sd); + else + return nullptr; } diff --git a/src/service_inspectors/dce_rpc/smb_message.h b/src/service_inspectors/dce_rpc/smb_message.h index 710907d30..019e63f7d 100644 --- a/src/service_inspectors/dce_rpc/smb_message.h +++ b/src/service_inspectors/dce_rpc/smb_message.h @@ -25,10 +25,8 @@ #include -namespace snort -{ -struct Packet; -} +#include "protocols/packet.h" +#include "smb_common.h" #pragma pack(1) @@ -2193,8 +2191,10 @@ inline uint16_t SmbWriteAndCloseRespCount(const SmbWriteAndCloseResp* resp) #pragma pack() void DCE2_SmbInitGlobals(); -void DCE2_SmbProcess(struct DCE2_SmbSsnData*); -DCE2_SmbSsnData* dce2_handle_smb_session(snort::Packet*, struct dce2SmbProtoConf*); +void DCE2_Smb1Process(struct DCE2_SmbSsnData*); +struct DCE2_SmbSsnData* dce2_create_new_smb_session(snort::Packet*, struct dce2SmbProtoConf*); +struct DCE2_Smb2SsnData* dce2_create_new_smb2_session(snort::Packet*, struct dce2SmbProtoConf*); +void DCE2_SmbDataFree(DCE2_SmbSsnData*); #endif