From: Lokesh Bevinamarad (lbevinam) Date: Tue, 16 Mar 2021 07:55:49 +0000 (+0000) Subject: Merge pull request #2737 in SNORT/snort3 from ~DIPANDIT/snort3:classify to master X-Git-Tag: 3.1.3.0~16 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f88ebffffeb078a6eb23513d7a3ae10aae6d8278;p=thirdparty%2Fsnort3.git Merge pull request #2737 in SNORT/snort3 from ~DIPANDIT/snort3:classify to master Squashed commit of the following: commit 85f29b509d5b53795caffbd55a44991929bac49c Author: Dipto Pandit Date: Thu Oct 8 06:55:59 2020 -0400 dce_rpc: refactoring smb code Changed old C style code to C++ code. Created classes for appropriate structures and encapsulated the methods. maintained data boundary as much as possible. Changed file structure to reduce clutter. --- diff --git a/src/service_inspectors/dce_rpc/CMakeLists.txt b/src/service_inspectors/dce_rpc/CMakeLists.txt index a8b19ad04..3f4ca4b09 100644 --- a/src/service_inspectors/dce_rpc/CMakeLists.txt +++ b/src/service_inspectors/dce_rpc/CMakeLists.txt @@ -6,7 +6,6 @@ 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 @@ -19,18 +18,25 @@ set( FILE_LIST dce_http_server_module.h dce_http_server_splitter.cc dce_http_server_splitter.h - dce_list.h dce_list.cc - dce_smb.cc - dce_smb.h + dce_list.h + dce_smb1.cc + dce_smb1.h dce_smb2.cc dce_smb2.h - dce_smb2_commands.cc - dce_smb2_commands.h - dce_smb2_utils.cc - dce_smb2_utils.h + dce_smb2_file.cc + dce_smb2_file.h + dce_smb2_request.h + dce_smb2_session.cc + dce_smb2_session.h + dce_smb2_tree.cc + dce_smb2_tree.h dce_smb_commands.cc dce_smb_commands.h + dce_smb_common.cc + dce_smb_common.h + dce_smb_inspector.cc + dce_smb_inspector.h dce_smb_module.cc dce_smb_module.h dce_smb_paf.cc diff --git a/src/service_inspectors/dce_rpc/dce_co.cc b/src/service_inspectors/dce_rpc/dce_co.cc index ac8b121fe..1bca583d3 100644 --- a/src/service_inspectors/dce_rpc/dce_co.cc +++ b/src/service_inspectors/dce_rpc/dce_co.cc @@ -28,7 +28,7 @@ #include "utils/util.h" #include "dce_expected_session.h" -#include "dce_smb.h" +#include "dce_smb1.h" #include "dce_smb_module.h" #include "dce_smb_utils.h" #include "dce_tcp.h" diff --git a/src/service_inspectors/dce_rpc/dce_common.cc b/src/service_inspectors/dce_rpc/dce_common.cc index c56a74b1b..99ea498dd 100644 --- a/src/service_inspectors/dce_rpc/dce_common.cc +++ b/src/service_inspectors/dce_rpc/dce_common.cc @@ -32,6 +32,8 @@ #include "dce_context_data.h" #include "dce_http_proxy_module.h" #include "dce_http_server_module.h" +#include "dce_smb1.h" +#include "dce_smb_common.h" #include "dce_smb_utils.h" #include "dce_tcp.h" #include "dce_udp.h" @@ -141,6 +143,7 @@ bool dce2_paf_abort(DCE2_SsnData* sd) void DCE2_Detect(DCE2_SsnData* sd) { + if (!sd) return ; DceContextData::set_current_ropts(sd); if ( using_rpkt ) { diff --git a/src/service_inspectors/dce_rpc/dce_common.h b/src/service_inspectors/dce_rpc/dce_common.h index 90d8c19d8..0e8ebf5b4 100644 --- a/src/service_inspectors/dce_rpc/dce_common.h +++ b/src/service_inspectors/dce_rpc/dce_common.h @@ -224,7 +224,7 @@ public: void reset(); }; -inline void DCE2_ResetRopts(DCE2_SsnData* sd, snort::Packet* p) +inline void DCE2_ResetRopts(DCE2_SsnData* sd, const snort::Packet* p) { sd->ropts.first_frag = DCE2_SENTINEL; sd->ropts.opnum = DCE2_SENTINEL; diff --git a/src/service_inspectors/dce_rpc/dce_db.h b/src/service_inspectors/dce_rpc/dce_db.h deleted file mode 100644 index 091fb1d61..000000000 --- a/src/service_inspectors/dce_rpc/dce_db.h +++ /dev/null @@ -1,129 +0,0 @@ -//-------------------------------------------------------------------------- -// 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" - -template -class DCE2_Db -{ -public: - - virtual void SetDoNotFree() = 0; - virtual bool 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() = default; - - ~DCE2_DbMap() - { - auto it = Map.cbegin(); - while (it != Map.cend()) - { - if (!do_not_free) - delete it->second; - it = Map.erase(it); - } - } - - void SetDoNotFree() override; - bool Insert(const Key& key, Value data) override; - Value Find(const Key& key) override; - void Remove(const Key& key) override; - int GetSize() override - { - return Map.size(); - } - - std::vector< std::pair > get_all_entry() override; - -private: - std::unordered_map Map; - bool do_not_free = false; -}; - -template -void DCE2_DbMap::SetDoNotFree() -{ - do_not_free = true; -} - -template -bool DCE2_DbMap::Insert(const Key& key, Value data) -{ - return Map.insert(std::make_pair(key,data)).second; -} - -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 (!do_not_free) - 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.h b/src/service_inspectors/dce_rpc/dce_smb.h deleted file mode 100644 index 51899c7a5..000000000 --- a/src/service_inspectors/dce_rpc/dce_smb.h +++ /dev/null @@ -1,521 +0,0 @@ -//-------------------------------------------------------------------------- -// Copyright (C) 2016-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_smb.h author Rashmi Pitre -// based on work by Todd Wease - -#ifndef DCE_SMB_H -#define DCE_SMB_H - -#include "dce_co.h" -#include "protocols/packet.h" -#include "profiler/profiler_defs.h" -#include "smb_common.h" -#include "smb_message.h" - -#define DCE2_SMB_NAME "dce_smb" -#define DCE2_SMB_HELP "dce over smb inspection" -#define DCE2_SMB_RPKT_TYPE_MAX 4 -#define DCE2_SMB_RPKT_TYPE_START 1 - -#define DCE2_SMB_BAD_NBSS_TYPE 2 -#define DCE2_SMB_BAD_TYPE 3 -#define DCE2_SMB_BAD_ID 4 -#define DCE2_SMB_BAD_WCT 5 -#define DCE2_SMB_BAD_BCC 6 -#define DCE2_SMB_BAD_FORM 7 -#define DCE2_SMB_BAD_OFF 8 -#define DCE2_SMB_TDCNT_ZE 9 -#define DCE2_SMB_NB_LT_SMBHDR 10 -#define DCE2_SMB_NB_LT_COM 11 -#define DCE2_SMB_NB_LT_BCC 12 -#define DCE2_SMB_NB_LT_DSIZE 13 -#define DCE2_SMB_TDCNT_LT_DSIZE 14 -#define DCE2_SMB_DSENT_GT_TDCNT 15 -#define DCE2_SMB_BCC_LT_DSIZE 16 -#define DCE2_SMB_INVALID_DSIZE 17 -#define DCE2_SMB_EXCESSIVE_TREE_CONNECTS 18 -#define DCE2_SMB_EXCESSIVE_READS 19 -#define DCE2_SMB_EXCESSIVE_CHAINING 20 -#define DCE2_SMB_MULT_CHAIN_SS 21 -#define DCE2_SMB_MULT_CHAIN_TC 22 -#define DCE2_SMB_CHAIN_SS_LOGOFF 23 -#define DCE2_SMB_CHAIN_TC_TDIS 24 -#define DCE2_SMB_CHAIN_OPEN_CLOSE 25 -#define DCE2_SMB_INVALID_SHARE 26 - -#define DCE2_SMB_V1 44 -#define DCE2_SMB_V2 45 -#define DCE2_SMB_INVALID_BINDING 46 -#define DCE2_SMB2_EXCESSIVE_COMPOUNDING 47 -#define DCE2_SMB_DCNT_ZERO 48 -#define DCE2_SMB_DCNT_MISMATCH 49 -#define DCE2_SMB_MAX_REQS_EXCEEDED 50 -#define DCE2_SMB_REQS_SAME_MID 51 -#define DCE2_SMB_DEPR_DIALECT_NEGOTIATED 52 -#define DCE2_SMB_DEPR_COMMAND_USED 53 -#define DCE2_SMB_UNUSUAL_COMMAND_USED 54 -#define DCE2_SMB_INVALID_SETUP_COUNT 55 -#define DCE2_SMB_MULTIPLE_NEGOTIATIONS 56 -#define DCE2_SMB_EVASIVE_FILE_ATTRS 57 -#define DCE2_SMB_INVALID_FILE_OFFSET 58 -#define DCE2_SMB_BAD_NEXT_COMMAND_OFFSET 59 - -#define DCE2_SMB_BAD_NBSS_TYPE_STR "SMB - bad NetBIOS session service session type" -#define DCE2_SMB_BAD_TYPE_STR "SMB - bad SMB message type" -#define DCE2_SMB_BAD_ID_STR "SMB - bad SMB Id (not \\xffSMB for SMB1 or not \\xfeSMB for SMB2)" -#define DCE2_SMB_BAD_WCT_STR "SMB - bad word count or structure size" -#define DCE2_SMB_BAD_BCC_STR "SMB - bad byte count" -#define DCE2_SMB_BAD_FORM_STR "SMB - bad format type" -#define DCE2_SMB_BAD_OFF_STR "SMB - bad offset" -#define DCE2_SMB_TDCNT_ZE_STR "SMB - zero total data count" -#define DCE2_SMB_NB_LT_SMBHDR_STR "SMB - NetBIOS data length less than SMB header length" -#define DCE2_SMB_NB_LT_COM_STR "SMB - remaining NetBIOS data length less than command length" -#define DCE2_SMB_NB_LT_BCC_STR "SMB - remaining NetBIOS data length less than command byte count" -#define DCE2_SMB_NB_LT_DSIZE_STR \ - "SMB - remaining NetBIOS data length less than command data size" -#define DCE2_SMB_TDCNT_LT_DSIZE_STR \ - "SMB - remaining total data count less than this command data size" -#define DCE2_SMB_DSENT_GT_TDCNT_STR \ - "SMB - total data sent (STDu64) greater than command total data expected" -#define DCE2_SMB_BCC_LT_DSIZE_STR "SMB - byte count less than command data size (STDu64)" -#define DCE2_SMB_INVALID_DSIZE_STR "SMB - invalid command data size for byte count" -#define DCE2_SMB_EXCESSIVE_TREE_CONNECTS_STR \ - "SMB - excessive tree connect requests with pending tree connect responses" -#define DCE2_SMB_EXCESSIVE_READS_STR "SMB - excessive read requests with pending read responses" -#define DCE2_SMB_EXCESSIVE_CHAINING_STR "SMB - excessive command chaining" -#define DCE2_SMB_MULT_CHAIN_SS_STR "SMB - multiple chained tree connect requests" -#define DCE2_SMB_MULT_CHAIN_TC_STR "SMB - multiple chained tree connect requests" -#define DCE2_SMB_CHAIN_SS_LOGOFF_STR "SMB - chained/compounded login followed by logoff" -#define DCE2_SMB_CHAIN_TC_TDIS_STR \ - "SMB - chained/compounded tree connect followed by tree disconnect" -#define DCE2_SMB_CHAIN_OPEN_CLOSE_STR \ - "SMB - chained/compounded open pipe followed by close pipe" -#define DCE2_SMB_INVALID_SHARE_STR "SMB - invalid share access" - -#define DCE2_SMB_V1_STR "SMB - invalid SMB version 1 seen" -#define DCE2_SMB_V2_STR "SMB - invalid SMB version 2 seen" -#define DCE2_SMB_INVALID_BINDING_STR "SMB - invalid user, tree connect, file binding" -#define DCE2_SMB2_EXCESSIVE_COMPOUNDING_STR "SMB - excessive command compounding" -#define DCE2_SMB_DCNT_ZERO_STR "SMB - zero data count" -#define DCE2_SMB_DCNT_MISMATCH_STR "SMB - data count mismatch in command and format" -#define DCE2_SMB_MAX_REQS_EXCEEDED_STR "SMB - maximum number of outstanding requests exceeded" -#define DCE2_SMB_REQS_SAME_MID_STR "SMB - outstanding requests with same MID" -#define DCE2_SMB_DEPR_DIALECT_NEGOTIATED_STR "SMB - deprecated dialect negotiated" -#define DCE2_SMB_DEPR_COMMAND_USED_STR "SMB - deprecated command used" -#define DCE2_SMB_UNUSUAL_COMMAND_USED_STR "SMB - unusual command used" -#define DCE2_SMB_INVALID_SETUP_COUNT_STR "SMB - invalid setup count for command" -#define DCE2_SMB_MULTIPLE_NEGOTIATIONS_STR \ - "SMB - client attempted multiple dialect negotiations on session" -#define DCE2_SMB_EVASIVE_FILE_ATTRS_STR \ - "SMB - client attempted to create or set a file's attributes to readonly/hidden/system" -#define DCE2_SMB_INVALID_FILE_OFFSET_STR \ - "SMB - file offset provided is greater than file size specified" -#define DCE2_SMB_BAD_NEXT_COMMAND_OFFSET_STR \ - "SMB - next command specified in SMB2 header is beyond payload boundary" - -struct dce2SmbStats -{ - PegCount events; - - PegCount co_pdus; - PegCount co_bind; - PegCount co_bind_ack; - PegCount co_alter_ctx; - PegCount co_alter_ctx_resp; - PegCount co_bind_nack; - PegCount co_request; - PegCount co_response; - PegCount co_cancel; - PegCount co_orphaned; - PegCount co_fault; - PegCount co_auth3; - PegCount co_shutdown; - PegCount co_reject; - PegCount co_ms_pdu; - PegCount co_other_req; - PegCount co_other_resp; - PegCount co_req_fragments; - PegCount co_resp_fragments; - PegCount co_cli_max_frag_size; - PegCount co_cli_min_frag_size; - PegCount co_cli_seg_reassembled; - PegCount co_cli_frag_reassembled; - PegCount co_srv_max_frag_size; - PegCount co_srv_min_frag_size; - PegCount co_srv_seg_reassembled; - PegCount co_srv_frag_reassembled; - - PegCount smb_sessions; - PegCount smb_pkts; - PegCount smb_ignored_bytes; - PegCount smb_cli_seg_reassembled; - PegCount smb_srv_seg_reassembled; - PegCount smb_max_outstanding_requests; - // FIXIT-M more peg count foo - /*uint64_t smb_com_stats[2][SMB_MAX_NUM_COMS]; - uint64_t smb_chained_stats[2][SMB_ANDX_COM__MAX][SMB_MAX_NUM_COMS]; - // The +1 is for codes beyond the range of the highest valid subcommand code - // Indicates a bogus subcommand - uint64_t smb_trans_subcom_stats[2][TRANS_SUBCOM_MAX+1]; - uint64_t smb_trans2_subcom_stats[2][TRANS2_SUBCOM_MAX+1]; - uint64_t smb_nt_transact_subcom_stats[2][NT_TRANSACT_SUBCOM_MAX+1]; - */ - PegCount smb_files_processed; - /* SMB2 stats */ - 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; -}; - -extern THREAD_LOCAL dce2SmbStats dce2_smb_stats; -extern THREAD_LOCAL snort::ProfileStats dce2_smb_pstat_main; - -enum DCE2_SmbSsnState -{ - DCE2_SMB_SSN_STATE__START = 0x00, - DCE2_SMB_SSN_STATE__NEGOTIATED = 0x01, - DCE2_SMB_SSN_STATE__FP_CLIENT = 0x02, // Fingerprinted client - DCE2_SMB_SSN_STATE__FP_SERVER = 0x04 // Fingerprinted server -}; - -enum DCE2_SmbDataState -{ - DCE2_SMB_DATA_STATE__NETBIOS_HEADER, - DCE2_SMB_DATA_STATE__SMB_HEADER, - DCE2_SMB_DATA_STATE__NETBIOS_PDU -}; - -enum DCE2_SmbPduState -{ - DCE2_SMB_PDU_STATE__COMMAND, - DCE2_SMB_PDU_STATE__RAW_DATA -}; - -enum DCE2_SmbFileDirection -{ - DCE2_SMB_FILE_DIRECTION__UNKNOWN = 0, - DCE2_SMB_FILE_DIRECTION__UPLOAD, - DCE2_SMB_FILE_DIRECTION__DOWNLOAD -}; - -enum SmbAndXCom -{ - SMB_ANDX_COM__NONE, - SMB_ANDX_COM__OPEN_ANDX, - SMB_ANDX_COM__READ_ANDX, - SMB_ANDX_COM__WRITE_ANDX, - SMB_ANDX_COM__TREE_CONNECT_ANDX, - SMB_ANDX_COM__SESSION_SETUP_ANDX, - SMB_ANDX_COM__LOGOFF_ANDX, - SMB_ANDX_COM__NT_CREATE_ANDX, - SMB_ANDX_COM__MAX -}; - -struct DCE2_SmbWriteAndXRaw -{ - int remaining; // A signed integer so it can be negative - DCE2_Buffer* buf; -}; - -struct DCE2_SmbFileChunk -{ - uint64_t offset; - uint32_t length; - uint8_t* data; -}; - -enum DCE2_SmbVersion -{ - DCE2_SMB_VERSION_NULL, - DCE2_SMB_VERSION_1, - DCE2_SMB_VERSION_2 -}; - -struct DCE2_SmbFileTracker -{ - union - { - struct - { - int file_id; // A signed integer so it can be set to sentinel - uint16_t u_id; - uint16_t tree_id; - } id_smb1; - - struct - { - uint64_t file_id; - } id_smb2; - } file_key; - - bool is_ipc; - bool is_smb2; - char* file_name; - uint16_t file_name_size; - uint64_t file_name_hash; - - union - { - struct - { - // If pipe has been set to byte mode via TRANS_SET_NMPIPE_STATE - bool byte_mode; - - // For Windows 2000 - bool used; - - // For WriteAndX requests that use raw mode flag - // Windows only - DCE2_SmbWriteAndXRaw* writex_raw; - - // Connection-oriented DCE/RPC tracker - DCE2_CoTracker* co_tracker; - } nmpipe; - - struct - { - uint64_t file_size; - uint64_t file_offset; - uint64_t bytes_processed; - DCE2_List* file_chunks; - uint32_t bytes_queued; - DCE2_SmbFileDirection file_direction; - bool sequential_only; - } 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 -#define fid_v2 file_key.id_smb2.file_id -#define fp_byte_mode tracker.nmpipe.byte_mode -#define fp_used tracker.nmpipe.used -#define fp_writex_raw tracker.nmpipe.writex_raw -#define fp_co_tracker tracker.nmpipe.co_tracker -#define ff_file_size tracker.file.file_size -#define ff_file_offset tracker.file.file_offset -#define ff_bytes_processed tracker.file.bytes_processed -#define ff_file_direction tracker.file.file_direction -#define ff_file_chunks tracker.file.file_chunks -#define ff_bytes_queued tracker.file.bytes_queued -#define ff_sequential_only tracker.file.sequential_only -}; - -struct Smb2Request -{ - uint64_t message_id; /* identifies a message uniquely on connection */ - uint64_t offset; /* data offset */ - uint64_t file_id; /* file id */ - struct Smb2Request* next; - struct Smb2Request* previous; -}; - -struct DCE2_SmbTransactionTracker -{ - int smb_type; - uint8_t subcom; - bool one_way; - bool disconnect_tid; - bool pipe_byte_mode; - uint32_t tdcnt; - uint32_t dsent; - DCE2_Buffer* dbuf; - uint32_t tpcnt; - uint32_t psent; - DCE2_Buffer* pbuf; - // For Transaction2/Query File Information - uint16_t info_level; -}; - -struct DCE2_SmbRequestTracker -{ - int smb_com; - - int mid; // A signed integer so it can be set to sentinel - uint16_t uid; - uint16_t tid; - uint16_t pid; - - // For WriteRaw - bool writeraw_writethrough; - uint32_t writeraw_remaining; - - // For Transaction/Transaction2/NtTransact - DCE2_SmbTransactionTracker ttracker; - - // Client can chain a write to an open. Need to write data, but also - // need to associate tracker with fid returned from server - DCE2_Queue* ft_queue; - - // This is a reference to an existing file tracker - DCE2_SmbFileTracker* ftracker; - - // Used for requests to cache data that will ultimately end up in - // the file tracker upon response. - char* file_name; - uint16_t file_name_size; - uint64_t file_size; - uint64_t file_offset; - bool sequential_only; - - // For TreeConnect to know whether it's to IPC - bool is_ipc; -}; - -struct DCE2_SmbSsnData -{ - DCE2_SsnData sd; // This member must be first - DCE2_Policy policy; - - int dialect_index; - int ssn_state_flags; - - DCE2_SmbDataState cli_data_state; - DCE2_SmbDataState srv_data_state; - - DCE2_SmbPduState pdu_state; - - int uid; // A signed integer so it can be set to sentinel - int tid; // A signed integer so it can be set to sentinel - DCE2_List* uids; - DCE2_List* tids; - - // For tracking files and named pipes - DCE2_SmbFileTracker ftracker; - DCE2_List* ftrackers; // List of DCE2_SmbFileTracker - - // For tracking requests / responses - DCE2_SmbRequestTracker rtracker; - DCE2_Queue* rtrackers; - uint16_t max_outstanding_requests; - uint16_t outstanding_requests; - - // The current pid/mid node for this request/response - DCE2_SmbRequestTracker* cur_rtracker; - - // Used for TCP segmentation to get full PDU - DCE2_Buffer* cli_seg; - DCE2_Buffer* srv_seg; - - // These are used for commands we don't need to process - uint32_t cli_ignore_bytes; - uint32_t srv_ignore_bytes; - - // The file API supports one concurrent upload/download per session. - // This is a reference to a file tracker so shouldn't be freed. - DCE2_SmbFileTracker* fapi_ftracker; - - DCE2_SmbFileTracker* fb_ftracker; - bool block_pdus; - - // Maximum file depth as returned from file API - int64_t max_file_depth; -}; - -struct DCE2_SmbFsm -{ - char input; - int next_state; - int fail_state; -}; - -class Dce2SmbFlowData : public snort::FlowData -{ -public: - Dce2SmbFlowData(); - ~Dce2SmbFlowData() override; - - static void init() - { inspector_id = snort::FlowData::create_flow_data_id(); } - - size_t size_of() override - { return sizeof(*this); } - -public: - static unsigned inspector_id; - DCE2_SmbVersion smb_version; - void* dce2_smb_session_data; -}; - -// Used for reassembled packets -#define DCE2_MOCK_HDR_LEN__SMB_CLI \ - ((unsigned)(sizeof(NbssHdr) + sizeof(SmbNtHdr) + sizeof(SmbWriteAndXReq))) -#define DCE2_MOCK_HDR_LEN__SMB_SRV \ - ((unsigned)(sizeof(NbssHdr) + sizeof(SmbNtHdr) + sizeof(SmbReadAndXResp))) - -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_smb.cc b/src/service_inspectors/dce_rpc/dce_smb1.cc similarity index 68% rename from src/service_inspectors/dce_rpc/dce_smb.cc rename to src/service_inspectors/dce_rpc/dce_smb1.cc index 10584f9ca..f4396e3a3 100644 --- a/src/service_inspectors/dce_rpc/dce_smb.cc +++ b/src/service_inspectors/dce_rpc/dce_smb1.cc @@ -1,5 +1,5 @@ //-------------------------------------------------------------------------- -// Copyright (C) 2016-2020 Cisco and/or its affiliates. All rights reserved. +// Copyright (C) 2020-2021 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 @@ -16,33 +16,16 @@ // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //-------------------------------------------------------------------------- -// dce_smb.cc author Rashmi Pitre +// dce_smb1.cc author Bhargava Jandhyala #ifdef HAVE_CONFIG_H #include "config.h" #endif -#include "dce_smb.h" - -#include "detection/detection_engine.h" -#include "file_api/file_service.h" -#include "main/snort_debug.h" -#include "managers/inspector_manager.h" -#include "protocols/packet.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_smb2.h" -#include "dce_smb2_utils.h" +#include "dce_smb1.h" using namespace snort; -THREAD_LOCAL dce2SmbStats dce2_smb_stats; -THREAD_LOCAL ProfileStats dce2_smb_pstat_main; - //------------------------------------------------------------------------- // debug stuff //------------------------------------------------------------------------- @@ -312,205 +295,27 @@ const char* get_smb_com_string(uint8_t b) { return smb_com_strings[b]; } #endif -//------------------------------------------------------------------------- -// class stuff -//------------------------------------------------------------------------- - -class Dce2Smb : public Inspector -{ -public: - Dce2Smb(const dce2SmbProtoConf&); - ~Dce2Smb() override; - - void show(const SnortConfig*) const override; - void eval(Packet*) override; - void clear(Packet*) override; - - StreamSplitter* get_splitter(bool c2s) override - { return new Dce2SmbSplitter(c2s); } - - bool can_carve_files() const override - { return true; } - -private: - dce2SmbProtoConf config; -}; - -Dce2Smb::Dce2Smb(const dce2SmbProtoConf& pc) -{ - config = pc; -} - -Dce2Smb::~Dce2Smb() -{ - if (config.smb_invalid_shares) - { - DCE2_ListDestroy(config.smb_invalid_shares); - } -} - -void Dce2Smb::show(const SnortConfig*) const -{ - print_dce2_smb_conf(config); -} - -void Dce2Smb::eval(Packet* p) -{ - 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); - - Dce2SmbFlowData *smb_flowdata = (Dce2SmbFlowData*)p->flow->get_flow_data(Dce2SmbFlowData::inspector_id); - if (smb_flowdata and smb_flowdata->dce2_smb_session_data) - { - 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); - debug_logf(dce_smb_trace, p, "smb1 session created\n"); - } - 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); - debug_logf(dce_smb_trace, p, "smb2 session created\n"); - } - 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. - debug_logf(dce_smb_trace, p, "non-smb packet detected\n"); - return; - } - } - - // 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); - } - 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_SsnData* sd = get_dce2_session_data(p->flow); - if ( sd ) - DCE2_ResetRopts(sd, p); -} - -//------------------------------------------------------------------------- -// api stuff -//------------------------------------------------------------------------- - -static Module* mod_ctor() -{ - return new Dce2SmbModule; -} - -static void mod_dtor(Module* m) -{ - delete m; -} - -static void dce2_smb_init() -{ - Dce2SmbFlowData::init(); - DCE2_SmbInitGlobals(); - DCE2_SmbInitDeletePdu(); - DceContextData::init(DCE2_TRANS_TYPE__SMB); -} - -static void dce2_smb_thread_int() -{ - DCE2_SmbSessionCacheInit(session_cache_size); -} - -static void dce_smb_thread_term() +Dce2Smb1SessionData::Dce2Smb1SessionData(const Packet* p, + const dce2SmbProtoConf* proto) : Dce2SmbSessionData(p, proto) { - delete smb2_session_cache; + ssd = { }; + ssd.max_outstanding_requests = 10; // Until Negotiate/SessionSetupAndX + ssd.cli_data_state = DCE2_SMB_DATA_STATE__NETBIOS_HEADER; + ssd.srv_data_state = DCE2_SMB_DATA_STATE__NETBIOS_HEADER; + ssd.pdu_state = DCE2_SMB_PDU_STATE__COMMAND; + ssd.uid = DCE2_SENTINEL; + ssd.tid = DCE2_SENTINEL; + ssd.ftracker.fid_v1 = DCE2_SENTINEL; + ssd.rtracker.mid = DCE2_SENTINEL; + ssd.dialect_index = dialect_index; + ssd.max_file_depth = max_file_depth; + ssd.sd = sd; + ssd.policy = policy; + debug_logf(dce_smb_trace, p, "smb1 session created\n"); } -static size_t get_max_smb_session(dce2SmbProtoConf* config) +void Dce2Smb1SessionData::process() { - size_t smb_sess_storage_req = (sizeof(DCE2_Smb2SessionTracker) + - sizeof(DCE2_Smb2TreeTracker) + sizeof(DCE2_Smb2RequestTracker) + - (sizeof(DCE2_Smb2FileTracker) * SMB_AVG_FILES_PER_SESSION)); - - size_t max_smb_sess = DCE2_ScSmbMemcap(config); - - return (max_smb_sess/smb_sess_storage_req); + DCE2_Smb1Process(&ssd); } -static Inspector* dce2_smb_ctor(Module* m) -{ - Dce2SmbModule* mod = (Dce2SmbModule*)m; - dce2SmbProtoConf config; - mod->get_data(config); - session_cache_size = get_max_smb_session(&config); - return new Dce2Smb(config); -} - -static void dce2_smb_dtor(Inspector* p) -{ - delete p; -} - -const InspectApi dce2_smb_api = -{ - { - PT_INSPECTOR, - sizeof(InspectApi), - INSAPI_VERSION, - 0, - API_RESERVED, - API_OPTIONS, - DCE2_SMB_NAME, - DCE2_SMB_HELP, - mod_ctor, - mod_dtor - }, - IT_SERVICE, - PROTO_BIT__PDU, - nullptr, // buffers - "netbios-ssn", - dce2_smb_init, - nullptr, // pterm - dce2_smb_thread_int, // tinit - dce_smb_thread_term, // tterm - dce2_smb_ctor, - dce2_smb_dtor, - nullptr, // ssn - nullptr // reset -}; - diff --git a/src/service_inspectors/dce_rpc/dce_smb1.h b/src/service_inspectors/dce_rpc/dce_smb1.h new file mode 100644 index 000000000..716923e38 --- /dev/null +++ b/src/service_inspectors/dce_rpc/dce_smb1.h @@ -0,0 +1,286 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2021 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_smb1.h author Bhargava Jandhyala + +#ifndef DCE_SMB1_H +#define DCE_SMB1_H + +// This provides smb session data and SMBv1 specific trackers + +#include "protocols/packet.h" +#include "profiler/profiler_defs.h" + +#include "dce_co.h" +#include "dce_smb_common.h" +#include "dce_smb_module.h" +#include "smb_message.h" + +enum DCE2_SmbSsnState +{ + DCE2_SMB_SSN_STATE__START = 0x00, + DCE2_SMB_SSN_STATE__NEGOTIATED = 0x01, + DCE2_SMB_SSN_STATE__FP_CLIENT = 0x02, // Fingerprinted client + DCE2_SMB_SSN_STATE__FP_SERVER = 0x04 // Fingerprinted server +}; + +enum DCE2_SmbDataState +{ + DCE2_SMB_DATA_STATE__NETBIOS_HEADER, + DCE2_SMB_DATA_STATE__SMB_HEADER, + DCE2_SMB_DATA_STATE__NETBIOS_PDU +}; + +enum DCE2_SmbFileDirection +{ + DCE2_SMB_FILE_DIRECTION__UNKNOWN = 0, + DCE2_SMB_FILE_DIRECTION__UPLOAD, + DCE2_SMB_FILE_DIRECTION__DOWNLOAD +}; + +enum SmbAndXCom +{ + SMB_ANDX_COM__NONE, + SMB_ANDX_COM__OPEN_ANDX, + SMB_ANDX_COM__READ_ANDX, + SMB_ANDX_COM__WRITE_ANDX, + SMB_ANDX_COM__TREE_CONNECT_ANDX, + SMB_ANDX_COM__SESSION_SETUP_ANDX, + SMB_ANDX_COM__LOGOFF_ANDX, + SMB_ANDX_COM__NT_CREATE_ANDX, + SMB_ANDX_COM__MAX +}; + +struct DCE2_SmbWriteAndXRaw +{ + int remaining; // A signed integer so it can be negative + DCE2_Buffer* buf; +}; + +struct DCE2_SmbFileChunk +{ + uint64_t offset; + uint32_t length; + uint8_t* data; +}; + +struct DCE2_SmbFileTracker +{ + struct + { + int file_id; // A signed integer so it can be set to sentinel + uint16_t u_id; + uint16_t tree_id; + } file_key; + + bool is_ipc; + bool is_smb2; + char* file_name; + uint16_t file_name_size; + uint64_t file_name_hash; + + union + { + struct + { + // If pipe has been set to byte mode via TRANS_SET_NMPIPE_STATE + bool byte_mode; + + // For Windows 2000 + bool used; + + // For WriteAndX requests that use raw mode flag + // Windows only + DCE2_SmbWriteAndXRaw* writex_raw; + + // Connection-oriented DCE/RPC tracker + DCE2_CoTracker* co_tracker; + } nmpipe; + + struct + { + uint64_t file_size; + uint64_t file_offset; + uint64_t bytes_processed; + DCE2_List* file_chunks; + uint32_t bytes_queued; + DCE2_SmbFileDirection file_direction; + bool sequential_only; + } file; + } tracker; + +#define fid_v1 file_key.file_id +#define uid_v1 file_key.u_id +#define tid_v1 file_key.tree_id +#define fp_byte_mode tracker.nmpipe.byte_mode +#define fp_used tracker.nmpipe.used +#define fp_writex_raw tracker.nmpipe.writex_raw +#define fp_co_tracker tracker.nmpipe.co_tracker +#define ff_file_size tracker.file.file_size +#define ff_file_offset tracker.file.file_offset +#define ff_bytes_processed tracker.file.bytes_processed +#define ff_file_direction tracker.file.file_direction +#define ff_file_chunks tracker.file.file_chunks +#define ff_bytes_queued tracker.file.bytes_queued +#define ff_sequential_only tracker.file.sequential_only +}; + +struct Smb2Request +{ + uint64_t message_id; /* identifies a message uniquely on connection */ + uint64_t offset; /* data offset */ + uint64_t file_id; /* file id */ + struct Smb2Request* next; + struct Smb2Request* previous; +}; + +struct DCE2_SmbTransactionTracker +{ + int smb_type; + uint8_t subcom; + bool one_way; + bool disconnect_tid; + bool pipe_byte_mode; + uint32_t tdcnt; + uint32_t dsent; + DCE2_Buffer* dbuf; + uint32_t tpcnt; + uint32_t psent; + DCE2_Buffer* pbuf; + // For Transaction2/Query File Information + uint16_t info_level; +}; + +struct DCE2_SmbRequestTracker +{ + int smb_com; + + int mid; // A signed integer so it can be set to sentinel + uint16_t uid; + uint16_t tid; + uint16_t pid; + + // For WriteRaw + bool writeraw_writethrough; + uint32_t writeraw_remaining; + + // For Transaction/Transaction2/NtTransact + DCE2_SmbTransactionTracker ttracker; + + // Client can chain a write to an open. Need to write data, but also + // need to associate tracker with fid returned from server + DCE2_Queue* ft_queue; + + // This is a reference to an existing file tracker + DCE2_SmbFileTracker* ftracker; + + // Used for requests to cache data that will ultimately end up in + // the file tracker upon response. + char* file_name; + uint16_t file_name_size; + uint64_t file_size; + uint64_t file_offset; + bool sequential_only; + + // For TreeConnect to know whether it's to IPC + bool is_ipc; +}; + +struct DCE2_SmbSsnData +{ + DCE2_SsnData sd; // This member must be first + DCE2_Policy policy; + + int dialect_index; + int ssn_state_flags; + + DCE2_SmbDataState cli_data_state; + DCE2_SmbDataState srv_data_state; + + Dce2SmbPduState pdu_state; + + int uid; // A signed integer so it can be set to sentinel + int tid; // A signed integer so it can be set to sentinel + DCE2_List* uids; + DCE2_List* tids; + + // For tracking files and named pipes + DCE2_SmbFileTracker ftracker; + DCE2_List* ftrackers; // List of DCE2_SmbFileTracker + + // For tracking requests / responses + DCE2_SmbRequestTracker rtracker; + DCE2_Queue* rtrackers; + uint16_t max_outstanding_requests; + uint16_t outstanding_requests; + + // The current pid/mid node for this request/response + DCE2_SmbRequestTracker* cur_rtracker; + + // Used for TCP segmentation to get full PDU + DCE2_Buffer* cli_seg; + DCE2_Buffer* srv_seg; + + // These are used for commands we don't need to process + uint32_t cli_ignore_bytes; + uint32_t srv_ignore_bytes; + + // The file API supports one concurrent upload/download per session. + // This is a reference to a file tracker so shouldn't be freed. + DCE2_SmbFileTracker* fapi_ftracker; + + DCE2_SmbFileTracker* fb_ftracker; + bool block_pdus; + + // Maximum file depth as returned from file API + int64_t max_file_depth; +}; + +struct DCE2_SmbFsm +{ + char input; + int next_state; + int fail_state; +}; + +// Used for reassembled packets +#define DCE2_MOCK_HDR_LEN__SMB_CLI \ + ((unsigned)(sizeof(NbssHdr) + sizeof(SmbNtHdr) + sizeof(SmbWriteAndXReq))) +#define DCE2_MOCK_HDR_LEN__SMB_SRV \ + ((unsigned)(sizeof(NbssHdr) + sizeof(SmbNtHdr) + sizeof(SmbReadAndXResp))) + +DCE2_SsnData* get_dce2_session_data(snort::Flow*); + +const char* get_smb_com_string(uint8_t); + +class Dce2Smb1SessionData : public Dce2SmbSessionData +{ +public: + Dce2Smb1SessionData() = delete; + Dce2Smb1SessionData(const snort::Packet*, const dce2SmbProtoConf* proto); + ~Dce2Smb1SessionData() override + { DCE2_SmbDataFree(&ssd); } + void process() override; + void handle_retransmit(FilePosition, FileVerdict) override { } + +private: + DCE2_SmbSsnData ssd; +}; + +#endif + diff --git a/src/service_inspectors/dce_rpc/dce_smb2.cc b/src/service_inspectors/dce_rpc/dce_smb2.cc index 9b3f3db74..553f17122 100644 --- a/src/service_inspectors/dce_rpc/dce_smb2.cc +++ b/src/service_inspectors/dce_rpc/dce_smb2.cc @@ -1,5 +1,5 @@ //-------------------------------------------------------------------------- -// Copyright (C) 2015-2020 Cisco and/or its affiliates. All rights reserved. +// Copyright (C) 2015-2021 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 @@ -24,13 +24,19 @@ #endif #include "dce_smb2.h" -#include "dce_smb2_commands.h" + #include "detection/detection_util.h" #include "flow/flow_key.h" -#include "main/snort_debug.h" + +#include "dce_smb2_file.h" +#include "dce_smb2_session.h" +#include "dce_smb2_session_cache.h" +#include "dce_smb2_tree.h" using namespace snort; +THREAD_LOCAL Dce2Smb2SessionCache* smb2_session_cache; + const char* smb2_command_string[SMB2_COM_MAX] = { "SMB2_COM_NEGOTIATE", "SMB2_COM_SESSION_SETUP", @@ -50,11 +56,22 @@ const char* smb2_command_string[SMB2_COM_MAX] = { "SMB2_COM_CHANGE_NOTIFY", "SMB2_COM_QUERY_INFO", "SMB2_COM_SET_INFO", - "SMB2_COM_OPLOCK_BREAK"}; + "SMB2_COM_OPLOCK_BREAK" +}; + +static inline uint64_t Smb2Sid(const Smb2Hdr* hdr) +{ + return alignedNtohq(&(hdr->session_id)); +} + +static inline bool Smb2Error(const Smb2Hdr* hdr) +{ + return (SMB_NT_STATUS_SEVERITY__ERROR == (uint8_t)(hdr->status >> 30)); +} -static inline SmbFlowKey get_flow_key() +Smb2FlowKey get_smb2_flow_key(void) { - SmbFlowKey key; + Smb2FlowKey key; const FlowKey* flow_key = DetectionEngine::get_current_packet()->flow->key; key.ip_l[0] = flow_key->ip_l[0]; @@ -80,227 +97,308 @@ static inline SmbFlowKey get_flow_key() return key; } -DCE2_Smb2RequestTracker::DCE2_Smb2RequestTracker(uint64_t file_id_v, - uint64_t offset_v) : file_id(file_id_v), offset(offset_v) +//Dce2Smb2SessionData member functions + +Dce2Smb2SessionData::Dce2Smb2SessionData(const Packet* p, + const dce2SmbProtoConf* proto) : Dce2SmbSessionData(p, proto) { - debug_logf(dce_smb_trace, nullptr, "request tracker created\n"); - memory::MemoryCap::update_allocations(sizeof(*this)); + tcp_file_tracker = nullptr; + flow_key = get_smb2_flow_key(); + debug_logf(dce_smb_trace, p, "smb2 session created\n"); } -DCE2_Smb2RequestTracker::DCE2_Smb2RequestTracker(char* fname_v, - uint16_t fname_len_v) : fname(fname_v), fname_len(fname_len_v) +Dce2Smb2SessionData::~Dce2Smb2SessionData(void) { - debug_logf(dce_smb_trace, nullptr, "request tracker created\n"); - memory::MemoryCap::update_allocations(sizeof(*this)); + for (auto it_session : connected_sessions) + { + if (it_session.second->detach_flow(flow_key)) + smb2_session_cache->remove(it_session.second->get_key()); + } } -DCE2_Smb2RequestTracker::~DCE2_Smb2RequestTracker() +void Dce2Smb2SessionData::reset_matching_tcp_file_tracker( + Dce2Smb2FileTracker* file_tracker) { - debug_logf(dce_smb_trace, nullptr, "request tracker terminating\n"); - if (!file_id and fname) - snort_free(fname); - memory::MemoryCap::update_deallocations(sizeof(*this)); + if (tcp_file_tracker == file_tracker) + tcp_file_tracker = nullptr; } -DCE2_Smb2FileTracker::DCE2_Smb2FileTracker(uint64_t file_id_v, DCE2_Smb2TreeTracker* ttr_v, - DCE2_Smb2SessionTracker* str_v, Flow* flow_v) : file_id(file_id_v), ttr(ttr_v), - str(str_v), flow(flow_v) +Smb2SessionKey Dce2Smb2SessionData::get_session_key(uint64_t session_id) { - debug_logf(dce_smb_trace, nullptr, "file tracker %" PRIu64 " created\n", file_id); - memory::MemoryCap::update_allocations(sizeof(*this)); + Smb2SessionKey key; + Flow* flow = DetectionEngine::get_current_packet()->flow; + memcpy(key.cip, flow->client_ip.get_ip6_ptr(), 4*sizeof(uint32_t)); + memcpy(key.sip, flow->server_ip.get_ip6_ptr(), 4*sizeof(uint32_t)); + key.sid = session_id; + key.cgroup = flow->client_group; + key.sgroup = flow->server_group; + key.asid = flow->key->addressSpaceId; + key.padding = 0; + return key; } -DCE2_Smb2FileTracker::~DCE2_Smb2FileTracker() +Dce2Smb2SessionTracker* Dce2Smb2SessionData::find_session(uint64_t session_id) { - debug_logf(dce_smb_trace, nullptr, - "file tracker %" PRIu64 " file name hash %" PRIu64 " terminating\n", - file_id, file_name_hash); - - FileFlows* file_flows = FileFlows::get_file_flows(flow, false); - if (file_flows) + auto it_session = connected_sessions.find(session_id); + if (it_session != connected_sessions.end()) { - file_flows->remove_processed_file_context(file_name_hash, file_id); + Dce2Smb2SessionTracker* session = it_session->second; + //we already have the session, but call find to update the LRU + smb2_session_cache->find_session(session->get_key()); + return session; } - - if (file_name) - snort_free((void*)file_name); - - memory::MemoryCap::update_deallocations(sizeof(*this)); -} - -DCE2_Smb2TreeTracker::DCE2_Smb2TreeTracker (uint32_t tid_v, uint8_t share_type_v) : - share_type(share_type_v), tid(tid_v) -{ - debug_logf(dce_smb_trace, nullptr, "tree tracker %" PRIu32 " created\n", tid); - memory::MemoryCap::update_allocations(sizeof(*this)); -} - -DCE2_Smb2TreeTracker::~DCE2_Smb2TreeTracker() -{ - debug_logf(dce_smb_trace, nullptr, "tree tracker %" PRIu32 " terminating\n", tid); - - auto all_req_trackers = req_trackers.get_all_entry(); - if (all_req_trackers.size()) + else { - debug_logf(dce_smb_trace, nullptr, "cleanup pending requests for below MIDs:\n"); - for ( auto& h : all_req_trackers ) + Dce2Smb2SessionTracker* session = smb2_session_cache->find_session( + get_session_key(session_id)); + if (session) { - debug_logf(dce_smb_trace, nullptr, "mid %" PRIu64 "\n", h.first); - removeRtracker(h.first); + session->attach_flow(flow_key, this); + connected_sessions.insert(std::make_pair(session_id,session)); } + return session; } - memory::MemoryCap::update_deallocations(sizeof(*this)); } -DCE2_Smb2SessionTracker::DCE2_Smb2SessionTracker() +// Caller must ensure that the session is not already present in flow +Dce2Smb2SessionTracker* Dce2Smb2SessionData::create_session(uint64_t session_id) { - debug_logf(dce_smb_trace, nullptr, "session tracker %" PRIu64 " created\n", session_id); - memory::MemoryCap::update_allocations(sizeof(*this)); + Smb2SessionKey session_key = get_session_key(session_id); + Dce2Smb2SessionTracker* session = smb2_session_cache->find_else_create_session(session_key); + session->init(session_id, session_key); + session->attach_flow(flow_key, this); + connected_sessions.insert(std::make_pair(session_id, session)); + return session; } -DCE2_Smb2SessionTracker::~DCE2_Smb2SessionTracker() +void Dce2Smb2SessionData::remove_session(uint64_t session_id) { - debug_logf(dce_smb_trace, nullptr, "session tracker %" PRIu64 " terminating\n", session_id); - removeSessionFromAllConnection(); - memory::MemoryCap::update_deallocations(sizeof(*this)); + connected_sessions.erase(session_id); + smb2_session_cache->remove(get_session_key(session_id)); } -void DCE2_Smb2SessionTracker::removeSessionFromAllConnection() +void Dce2Smb2SessionData::process_command(const Smb2Hdr* smb_hdr, + const uint8_t* end) { - auto all_conn_trackers = conn_trackers.get_all_entry(); - auto all_tree_trackers = tree_trackers.get_all_entry(); - for ( auto& h : all_conn_trackers ) - { - if (h.second->ftracker_tcp) - { - for (auto& t : all_tree_trackers) - { - DCE2_Smb2FileTracker* ftr = t.second->findFtracker( - h.second->ftracker_tcp->file_id); - if (ftr and ftr == h.second->ftracker_tcp) - { - h.second->ftracker_tcp = nullptr; - break; - } - } - } - DCE2_Smb2RemoveSidInSsd(h.second, session_id); + const uint8_t* smb_data = (const uint8_t*)smb_hdr + SMB2_HEADER_LENGTH; + uint16_t structure_size = alignedNtohs((const uint16_t*)smb_data); + +// Macro and shorthand to save some repetitive code +// Should only be used in this function +#define SMB2_COMMAND_TYPE(cmd, type) \ + (structure_size == SMB2_ ## cmd ## _ ## type ## _STRUC_SIZE) + +#define SMB2_GET_COMMAND_TYPE(cmd) \ + (SMB2_COMMAND_TYPE(ERROR,RESPONSE) and Smb2Error(smb_hdr)) ? \ + SMB2_CMD_TYPE_ERROR_RESPONSE : (SMB2_COMMAND_TYPE(cmd, REQUEST) ? \ + SMB2_CMD_TYPE_REQUEST : (SMB2_COMMAND_TYPE(cmd, RESPONSE) ? \ + SMB2_CMD_TYPE_RESPONSE : SMB2_CMD_TYPE_INVALID)) + +#define SMB2_HANDLE_HEADER_ERROR(cmd, type, counter) \ + { \ + if (smb_data + SMB2_ ## cmd ## _ ## type ## _STRUC_SIZE - 1 > end) \ + { \ + dce2_smb_stats.v2_ ## counter ## _hdr_err++; \ + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, \ + "%s : smb data beyond end detected\n", \ + smb2_command_string[command]); \ + return; \ + } \ } -} - -static inline bool DCE2_Smb2FindSidTid(DCE2_Smb2SsnData* ssd, const uint64_t sid, - const uint32_t tid, const uint32_t mid, DCE2_Smb2SessionTracker** str, DCE2_Smb2TreeTracker** ttr) -{ - *str = DCE2_Smb2FindSidInSsd(ssd, sid); - if (!*str) - return false; - - if(!tid) - *ttr = find_tree_for_message(*str, mid); - else - *ttr = (*str)->findTtracker(tid); - - if(!*ttr) - return false; - return true; -} +#define SMB2_HANDLE_ERROR_RESPONSE(counter) \ + { \ + if (SMB2_COMMAND_TYPE(ERROR, RESPONSE) and Smb2Error(smb_hdr)) \ + { \ + dce2_smb_stats.v2_ ## counter ## _err_resp++; \ + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, "%s_RESP: error\n", \ + smb2_command_string[command]); \ + return; \ + } \ + } -// FIXIT-L port fileCache related code along with -// DCE2_Smb2Init, DCE2_Smb2Close and DCE2_Smb2UpdateStats +#define SMB2_HANDLE_INVALID_STRUC_SIZE(counter) \ + { \ + dce2_smb_stats.v2_ ## counter ## _inv_str_sz++; \ + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, "%s: invalid struct size\n", \ + smb2_command_string[command]); \ + return; \ + } -static void DCE2_Smb2Inspect(DCE2_Smb2SsnData* 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)); - DCE2_Smb2SessionTracker* str = nullptr; - DCE2_Smb2TreeTracker* ttr = nullptr; - - uint64_t mid = Smb2Mid(smb_hdr); - uint64_t sid = Smb2Sid(smb_hdr); - uint32_t tid = Smb2Tid(smb_hdr); - - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), + uint64_t session_id = Smb2Sid(smb_hdr); + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, "%s : mid %" PRIu64 " sid %" PRIu64 " tid %" PRIu32 "\n", - (command <= SMB2_COM_OPLOCK_BREAK ? smb2_command_string[command] : "unknown"), - mid, sid, tid); + (command < SMB2_COM_MAX ? smb2_command_string[command] : "unknown"), + Smb2Mid(smb_hdr), session_id, Smb2Tid(smb_hdr)); + // Try to find the session. + // The case when session is not available will be handled per command. + Dce2Smb2SessionTracker* session = find_session(session_id); + switch (command) { - 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, mid, &str, &ttr) or - SMB2_SHARE_TYPE_DISK != ttr->get_share_type()) + //commands processed by flow + case SMB2_COM_SESSION_SETUP: + dce2_smb_stats.v2_setup++; + SMB2_HANDLE_ERROR_RESPONSE(setup) + if (SMB2_COMMAND_TYPE(SETUP, RESPONSE)) { - dce2_smb_stats.v2_read_ignored++; - return; + SMB2_HANDLE_HEADER_ERROR(SETUP, RESPONSE, setup_resp) + if (!session) + create_session(session_id); } + else if (!SMB2_COMMAND_TYPE(SETUP, REQUEST)) + SMB2_HANDLE_INVALID_STRUC_SIZE(setup) + break; - DCE2_Smb2Read(ssd, smb_hdr, smb_data, end, str, ttr, mid); + case SMB2_COM_LOGOFF: + dce2_smb_stats.v2_logoff++; + if (SMB2_COMMAND_TYPE(LOGOFF, REQUEST)) + remove_session(session_id); + else + SMB2_HANDLE_INVALID_STRUC_SIZE(logoff) break; - case SMB2_COM_WRITE: - dce2_smb_stats.v2_wrt++; - if (!DCE2_Smb2FindSidTid(ssd, sid, tid, mid, &str, &ttr) or - SMB2_SHARE_TYPE_DISK != ttr->get_share_type()) + //commands processed by session + case SMB2_COM_TREE_CONNECT: + dce2_smb_stats.v2_tree_cnct++; + + SMB2_HANDLE_ERROR_RESPONSE(tree_cnct) + if (SMB2_COMMAND_TYPE(TREE_CONNECT, RESPONSE)) { - dce2_smb_stats.v2_wrt_ignored++; - return; + SMB2_HANDLE_HEADER_ERROR(TREE_CONNECT, RESPONSE, tree_cnct_resp) + if (!session) + session = create_session(session_id); + session->process(command, SMB2_CMD_TYPE_RESPONSE, smb_hdr, end); } - - DCE2_Smb2Write(ssd, smb_hdr, smb_data, end, str, ttr, mid); + else if (!SMB2_COMMAND_TYPE(TREE_CONNECT,REQUEST)) + SMB2_HANDLE_INVALID_STRUC_SIZE(tree_cnct) break; - case SMB2_COM_SET_INFO: - dce2_smb_stats.v2_stinf++; - if (!DCE2_Smb2FindSidTid(ssd, sid, tid, mid, &str, &ttr) or - SMB2_SHARE_TYPE_DISK != ttr->get_share_type()) + + case SMB2_COM_TREE_DISCONNECT: + dce2_smb_stats.v2_tree_discn++; + + if (session) { - dce2_smb_stats.v2_stinf_ignored++; - return; + if (SMB2_COMMAND_TYPE(TREE_DISCONNECT, REQUEST)) + { + SMB2_HANDLE_HEADER_ERROR(TREE_DISCONNECT, REQUEST, tree_discn_req) + session->process(command, SMB2_CMD_TYPE_REQUEST, smb_hdr, end); + } + else + { + SMB2_HANDLE_INVALID_STRUC_SIZE(tree_discn) + } } + else + dce2_smb_stats.v2_session_ignored++; + break; + //commands processed by tree + case SMB2_COM_CREATE: + { + dce2_smb_stats.v2_crt++; - DCE2_Smb2SetInfo(ssd, smb_hdr, smb_data, end, ttr); + uint8_t command_type = SMB2_GET_COMMAND_TYPE(CREATE); + if (SMB2_CMD_TYPE_INVALID == command_type) + SMB2_HANDLE_INVALID_STRUC_SIZE(crt) + else if (SMB2_COMMAND_TYPE(CREATE, REQUEST)) + SMB2_HANDLE_HEADER_ERROR(CREATE, REQUEST, crt_req) + else if (SMB2_COMMAND_TYPE(CREATE, RESPONSE)) + SMB2_HANDLE_HEADER_ERROR(CREATE, RESPONSE, crt_resp) + + if (!session) + session = create_session(session_id); + session->process(command, command_type, smb_hdr, end); + } break; + case SMB2_COM_CLOSE: dce2_smb_stats.v2_cls++; - if (!DCE2_Smb2FindSidTid(ssd, sid, tid, mid, &str, &ttr) or - SMB2_SHARE_TYPE_DISK != ttr->get_share_type()) + SMB2_HANDLE_ERROR_RESPONSE(cls) + if (session) { - dce2_smb_stats.v2_cls_ignored++; - return; + if (SMB2_COMMAND_TYPE(CLOSE, REQUEST)) + { + SMB2_HANDLE_HEADER_ERROR(CLOSE, REQUEST, cls_req) + session->process(command, SMB2_CMD_TYPE_REQUEST, smb_hdr, end); + } + else if (!SMB2_COMMAND_TYPE(CLOSE, RESPONSE)) + { + SMB2_HANDLE_INVALID_STRUC_SIZE(cls) + } } - - DCE2_Smb2CloseCmd(ssd, smb_hdr, smb_data, end, ttr, str); + else + dce2_smb_stats.v2_session_ignored++; 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) + + case SMB2_COM_SET_INFO: + dce2_smb_stats.v2_setinfo++; + SMB2_HANDLE_ERROR_RESPONSE(stinf) + if (session) { - DCE2_Smb2TreeConnect(ssd, smb_hdr, smb_data, end, str, tid); + if (SMB2_COMMAND_TYPE(SET_INFO, REQUEST)) + { + SMB2_HANDLE_HEADER_ERROR(SET_INFO, REQUEST, stinf_req) + session->process(command, SMB2_CMD_TYPE_REQUEST, smb_hdr, end); + } + else if (!SMB2_COMMAND_TYPE(SET_INFO, RESPONSE)) + { + SMB2_HANDLE_INVALID_STRUC_SIZE(stinf) + } } + else + dce2_smb_stats.v2_session_ignored++; break; - case SMB2_COM_TREE_DISCONNECT: - dce2_smb_stats.v2_tree_discn++; - if (!DCE2_Smb2FindSidTid(ssd, sid, tid, mid, &str, &ttr)) + + case SMB2_COM_READ: + dce2_smb_stats.v2_read++; + if (session) { - dce2_smb_stats.v2_tree_discn_ignored++; - return; + uint8_t command_type; + if (SMB2_COMMAND_TYPE(ERROR, RESPONSE) and Smb2Error(smb_hdr)) + command_type = SMB2_CMD_TYPE_ERROR_RESPONSE; + else if (SMB2_COMMAND_TYPE(READ, REQUEST)) + { + SMB2_HANDLE_HEADER_ERROR(READ, REQUEST, read_req) + command_type = SMB2_CMD_TYPE_REQUEST; + } + else if (SMB2_COMMAND_TYPE(READ, RESPONSE)) + { + SMB2_HANDLE_HEADER_ERROR(READ, RESPONSE, read_resp) + command_type = SMB2_CMD_TYPE_RESPONSE; + } + else + SMB2_HANDLE_INVALID_STRUC_SIZE(read) + session->process(command, command_type, smb_hdr, end); } - DCE2_Smb2TreeDisconnect(ssd, smb_data, end, str, tid); - break; - case SMB2_COM_SESSION_SETUP: - dce2_smb_stats.v2_setup++; - DCE2_Smb2Setup(ssd, smb_hdr, sid, smb_data, end); + else + dce2_smb_stats.v2_session_ignored++; break; - case SMB2_COM_LOGOFF: - dce2_smb_stats.v2_logoff++; - DCE2_Smb2Logoff(ssd, smb_data, sid); + + case SMB2_COM_WRITE: + dce2_smb_stats.v2_wrt++; + if (session) + { + uint8_t command_type; + if (SMB2_COMMAND_TYPE(ERROR, RESPONSE) and Smb2Error(smb_hdr)) + command_type = SMB2_CMD_TYPE_ERROR_RESPONSE; + else if (SMB2_COMMAND_TYPE(WRITE, REQUEST)) + { + SMB2_HANDLE_HEADER_ERROR(WRITE, REQUEST, wrt_req) + command_type = SMB2_CMD_TYPE_REQUEST; + } + else if (SMB2_COMMAND_TYPE(WRITE, RESPONSE)) + { + SMB2_HANDLE_HEADER_ERROR(WRITE, RESPONSE, wrt_resp) + command_type = SMB2_CMD_TYPE_RESPONSE; + } + else + SMB2_HANDLE_INVALID_STRUC_SIZE(wrt) + session->process(command, command_type, smb_hdr, end); + } + else + dce2_smb_stats.v2_session_ignored++; break; + default: dce2_smb_stats.v2_msgs_uninspected++; break; @@ -308,7 +406,7 @@ static void DCE2_Smb2Inspect(DCE2_Smb2SsnData* ssd, const Smb2Hdr* smb_hdr, } // This is the main entry point for SMB2 processing. -void DCE2_Smb2Process(DCE2_Smb2SsnData* ssd) +void Dce2Smb2SessionData::process() { Packet* p = DetectionEngine::get_current_packet(); const uint8_t* data_ptr = p->data; @@ -317,8 +415,8 @@ void DCE2_Smb2Process(DCE2_Smb2SsnData* ssd) // Check header length if (data_len < sizeof(NbssHdr) + SMB2_HEADER_LENGTH) { - dce2_smb_stats.v2_hdr_err++; debug_logf(dce_smb_trace, p, "Header error with data length %d\n",data_len); + dce2_smb_stats.v2_hdr_err++; return; } @@ -338,13 +436,14 @@ void DCE2_Smb2Process(DCE2_Smb2SsnData* ssd) // multiple requests up to the maximum size<88> that is supported by the transport." do { - DCE2_Smb2Inspect(ssd, smb_hdr, data_ptr + data_len); + process_command(smb_hdr, data_ptr + data_len); + // 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); + (dce2CommonStats*)&dce2_smb_stats, sd); debug_logf(dce_smb_trace, p, "bad next command offset\n"); dce2_smb_stats.v2_bad_next_cmd_offset++; return; @@ -355,59 +454,23 @@ void DCE2_Smb2Process(DCE2_Smb2SsnData* ssd) compound_request_index++; } - if (compound_request_index > DCE2_ScSmbMaxCompound((dce2SmbProtoConf*)ssd->sd.config)) + if (compound_request_index > get_smb_max_compound()) { dce2_smb_stats.v2_cmpnd_req_lt_crossed++; - debug_logf(dce_smb_trace, p, "compound req limit reached %" PRIu8 "\n", - compound_request_index); + debug_logf(dce_smb_trace, p, "compound request limit" + " reached %" PRIu8 "\n",compound_request_index); return; } } while (next_command_offset and smb_hdr); } - else if ( ssd->ftracker_tcp and (ssd->ftracker_tcp->smb2_pdu_state == - DCE2_SMB_PDU_STATE__RAW_DATA)) - { - debug_logf(dce_smb_trace, p, - "raw data file_name_hash %" PRIu64 " fid %" PRIu64 " dir %s\n", - ssd->ftracker_tcp->file_name_hash, ssd->ftracker_tcp->file_id, - ssd->ftracker_tcp->upload ? "upload" : "download"); - - if (!DCE2_Smb2ProcessFileData(ssd, data_ptr, data_len)) - return; - ssd->ftracker_tcp->file_offset += data_len; - } -} - -DCE2_Ret DCE2_Smb2InitData(DCE2_Smb2SsnData* ssd) -{ - memset(&ssd->sd, 0, sizeof(DCE2_SsnData)); - ssd->session_trackers.SetDoNotFree(); - 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(); - ssd->flow_key = get_flow_key(); - return DCE2_RET__SUCCESS; -} - -// Check whether the packet is smb2 -DCE2_SmbVersion DCE2_Smb2Version(const Packet* p) -{ - // Only check reassembled SMB2 packet - if ( p->has_paf_payload() and - (p->dsize > sizeof(NbssHdr) + DCE2_SMB_ID_SIZE) ) // DCE2_SMB_ID is u32 + else if ( tcp_file_tracker and tcp_file_tracker->accepting_raw_data()) { - const Smb2Hdr* smb_hdr = (const Smb2Hdr*)(p->data + sizeof(NbssHdr)); - uint32_t smb_version_id = SmbId((const SmbNtHdr*)smb_hdr); - - if (smb_version_id == DCE2_SMB_ID) - return DCE2_SMB_VERSION_1; - else if (smb_version_id == DCE2_SMB2_ID) - return DCE2_SMB_VERSION_2; + debug_logf(dce_smb_trace, p, "processing raw data for file id %" PRIu64 "\n", + tcp_file_tracker->get_file_id()); + + if (!tcp_file_tracker->process_data(data_ptr, data_len)) + tcp_file_tracker->get_parent()->close_file(tcp_file_tracker->get_file_id()); } - - 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 490d7f8ac..642a53e69 100644 --- a/src/service_inspectors/dce_rpc/dce_smb2.h +++ b/src/service_inspectors/dce_rpc/dce_smb2.h @@ -22,15 +22,52 @@ #ifndef DCE_SMB2_H #define DCE_SMB2_H -#include "dce_db.h" -#include "dce_smb.h" -#include "hash/lru_cache_shared.h" +// This implements smb session data for SMBv2 +// Also provides SMBv2 related header structures + #include "main/thread_config.h" #include "memory/memory_cap.h" #include "utils/util.h" +#include "dce_smb_common.h" + +/* SMB2 command codes */ +#define SMB2_COM_NEGOTIATE 0x00 +#define SMB2_COM_SESSION_SETUP 0x01 +#define SMB2_COM_LOGOFF 0x02 +#define SMB2_COM_TREE_CONNECT 0x03 +#define SMB2_COM_TREE_DISCONNECT 0x04 +#define SMB2_COM_CREATE 0x05 +#define SMB2_COM_CLOSE 0x06 +#define SMB2_COM_FLUSH 0x07 +#define SMB2_COM_READ 0x08 +#define SMB2_COM_WRITE 0x09 +#define SMB2_COM_LOCK 0x0A +#define SMB2_COM_IOCTL 0x0B +#define SMB2_COM_CANCEL 0x0C +#define SMB2_COM_ECHO 0x0D +#define SMB2_COM_QUERY_DIRECTORY 0x0E +#define SMB2_COM_CHANGE_NOTIFY 0x0F +#define SMB2_COM_QUERY_INFO 0x10 +#define SMB2_COM_SET_INFO 0x11 +#define SMB2_COM_OPLOCK_BREAK 0x12 +#define SMB2_COM_MAX 0x13 + +extern const char* smb2_command_string[SMB2_COM_MAX]; + +// file attribute for create response +#define SMB2_CREATE_RESPONSE_DIRECTORY 0x10 #define SMB_AVG_FILES_PER_SESSION 5 +#define SMB2_SHARE_TYPE_DISK 0x01 +#define SMB2_SHARE_TYPE_PIPE 0x02 +#define SMB2_SHARE_TYPE_PRINT 0x03 + +#define SMB2_CMD_TYPE_ERROR_RESPONSE 0 +#define SMB2_CMD_TYPE_REQUEST 1 +#define SMB2_CMD_TYPE_RESPONSE 2 +#define SMB2_CMD_TYPE_INVALID 3 + struct Smb2Hdr { uint8_t smb_idf[4]; /* contains 0xFE,’SMB’ */ @@ -47,22 +84,6 @@ struct Smb2Hdr uint8_t signature[16]; /* signature of the message */ }; -struct Smb2ASyncHdr -{ - uint8_t smb_idf[4]; /* contains 0xFE,’SMB’ */ - uint16_t structure_size; /* This MUST be set to 64 */ - uint16_t credit_charge; /* # of credits that this request consumes */ - uint32_t status; /* depends */ - uint16_t command; /* command code */ - uint16_t credit; /* # of credits requesting/granted */ - uint32_t flags; /* flags */ - uint32_t next_command; /* used for compounded request */ - uint64_t message_id; /* identifies a message uniquely on connection */ - uint64_t async_id; /* handle operations asynchronously */ - uint64_t session_id; /* identifies the established session for the command*/ - uint8_t signature[16]; /* signature of the message */ -}; - struct Smb2SyncHdr { uint8_t smb_idf[4]; /* contains 0xFE,’SMB’ */ @@ -80,392 +101,6 @@ struct Smb2SyncHdr uint8_t signature[16]; /* signature of the message */ }; -struct Smb2ErrorResponseHdr -{ - uint16_t structure_size; /* This MUST be set to 9 */ - uint16_t reserved; /* reserved */ - uint32_t byte_count; /* The number of bytes of error_data */ - 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 file_id_v, uint64_t offset_v = 0); - DCE2_Smb2RequestTracker(char* fname_v, uint16_t fname_len_v); - ~DCE2_Smb2RequestTracker(); - - uint64_t get_offset() - { - return offset; - } - - uint64_t get_file_id() - { - return file_id; - } - - void set_file_id(uint64_t fid) - { - file_id = fid; - } - - char* fname = nullptr; - uint16_t fname_len = 0; - -private: - - uint64_t file_id = 0; - uint64_t offset = 0; -}; - -struct DCE2_Smb2SsnData; -class DCE2_Smb2SessionTracker; - -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, DCE2_Smb2TreeTracker* ttr_v, - DCE2_Smb2SessionTracker* str_v, snort::Flow* flow_v); - ~DCE2_Smb2FileTracker(); - - bool ignore = false; - bool upload = false; - uint16_t file_name_len = 0; - uint64_t bytes_processed = 0; - uint64_t file_offset = 0; - uint64_t file_id = 0; - uint64_t file_size = 0; - uint64_t file_name_hash = 0; - char* file_name = nullptr; - DCE2_SmbPduState smb2_pdu_state; - DCE2_Smb2TreeTracker* ttr = nullptr; - DCE2_Smb2SessionTracker* str = nullptr; - snort::Flow *flow = nullptr; -}; - -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); - ~DCE2_Smb2TreeTracker(); - - // File Tracker - DCE2_Smb2FileTracker* findFtracker(uint64_t file_id) - { - return file_trackers.Find(file_id); - } - - bool insertFtracker(uint64_t file_id, DCE2_Smb2FileTracker* ftracker) - { - return file_trackers.Insert(file_id, ftracker); - } - - void removeFtracker(uint64_t file_id) - { - file_trackers.Remove(file_id); - } - - // Request Tracker - DCE2_Smb2RequestTracker* findRtracker(uint64_t mid) - { - return req_trackers.Find(mid); - } - - bool insertRtracker(uint64_t message_id, DCE2_Smb2RequestTracker* rtracker) - { - return req_trackers.Insert(message_id, rtracker); - } - - void removeRtracker(uint64_t message_id) - { - req_trackers.Remove(message_id); - } - - int getRtrackerSize() - { - return req_trackers.GetSize(); - } - - // common methods - uint8_t get_share_type() - { - return share_type; - } - - uint32_t get_tid() - { - return tid; - } - -private: - uint8_t share_type = 0; - uint32_t tid = 0; - - DCE2_DbMapRtracker req_trackers; - DCE2_DbMapFtracker file_trackers; -}; - -PADDING_GUARD_BEGIN -struct Smb2SidHashKey -{ - //must be of size 3*x*sizeof(uint32_t) - uint32_t cip[4]; - uint32_t sip[4]; - uint64_t sid; - int16_t cgroup; - int16_t sgroup; - uint16_t asid; - uint16_t padding; - - bool operator== (const Smb2SidHashKey &other) const - { - return( sid == other.sid and - cip[0] == other.cip[0] and - cip[1] == other.cip[1] and - cip[2] == other.cip[2] and - cip[3] == other.cip[3] and - sip[0] == other.sip[0] and - sip[1] == other.sip[1] and - sip[2] == other.sip[2] and - sip[3] == other.sip[3] and - cgroup == other.cgroup and - sgroup == other.sgroup and - asid == other.asid); - } -}; - -struct SmbFlowKey -{ - uint32_t ip_l[4]; /* Low IP */ - uint32_t ip_h[4]; /* High IP */ - uint32_t mplsLabel; - uint16_t port_l; /* Low Port - 0 if ICMP */ - uint16_t port_h; /* High Port - 0 if ICMP */ - int16_t group_l; - int16_t group_h; - uint16_t vlan_tag; - uint16_t addressSpaceId; - uint8_t ip_protocol; - uint8_t pkt_type; - uint8_t version; - uint8_t padding; - - bool operator==(const SmbFlowKey& other) const - { - return (ip_l[0] == other.ip_l[0] and - ip_l[1] == other.ip_l[1] and - ip_l[2] == other.ip_l[2] and - ip_l[3] == other.ip_l[3] and - ip_h[0] == other.ip_h[0] and - ip_l[1] == other.ip_l[1] and - ip_l[2] == other.ip_l[2] and - ip_l[3] == other.ip_l[3] and - mplsLabel == other.mplsLabel and - port_l == other.port_l and - port_h == other.port_h and - group_l == other.group_l and - group_h == other.group_h and - vlan_tag == other.vlan_tag and - addressSpaceId == other.addressSpaceId and - ip_protocol == other.ip_protocol and - pkt_type == other.pkt_type and - version == other.version); - } -}; -PADDING_GUARD_END - -//The below value is taken from Hash Key class static hash hardener -#define SMB_KEY_HASH_HARDENER 133824503 - -struct SmbKeyHash -{ - size_t operator() (const SmbFlowKey& key) const - { - return do_hash_flow_key((const uint32_t*)&key); - } - - size_t operator() (const Smb2SidHashKey& key) const - { - return do_hash((const uint32_t*)&key); - } - -private: - size_t do_hash(const uint32_t* d) const - { - uint32_t a, b, c; - a = b = c = SMB_KEY_HASH_HARDENER; - a += d[0]; b += d[1]; c += d[2]; mix(a, b, c); - a += d[3]; b += d[4]; c += d[5]; mix(a, b, c); - a += d[6]; b += d[7]; c += d[8]; mix(a, b, c); - a += d[9]; b += d[10]; c += d[11]; finalize(a, b, c); - return c; - } - - size_t do_hash_flow_key(const uint32_t* d) const - { - uint32_t a, b, c; - a = b = c = SMB_KEY_HASH_HARDENER; - a += d[0]; b += d[1]; c += d[2]; mix(a, b, c); - a += d[3]; b += d[4]; c += d[5]; mix(a, b, c); - a += d[6]; b += d[7]; c += d[8]; mix(a, b, c); - a += d[9]; b += d[10]; c += d[11]; mix(a, b, c); - a += d[12]; finalize(a, b, c); - return c; - } - - inline uint32_t rot(uint32_t x, unsigned k) const - { return (x << k) | (x >> (32 - k)); } - - inline void mix(uint32_t& a, uint32_t& b, uint32_t& c) const - { - a -= c; a ^= rot(c, 4); c += b; - b -= a; b ^= rot(a, 6); a += c; - c -= b; c ^= rot(b, 8); b += a; - a -= c; a ^= rot(c,16); c += b; - b -= a; b ^= rot(a,19); a += c; - c -= b; c ^= rot(b, 4); b += a; - } - - inline void finalize(uint32_t& a, uint32_t& b, uint32_t& c) const - { - c ^= b; c -= rot(b,14); - a ^= c; a -= rot(c,11); - b ^= a; b -= rot(a,25); - c ^= b; c -= rot(b,16); - a ^= c; a -= rot(c,4); - b ^= a; b -= rot(a,14); - c ^= b; c -= rot(b,24); - } -}; - -typedef DCE2_DbMap > DCE2_DbMapTtracker; -typedef DCE2_DbMap DCE2_DbMapConntracker; -class DCE2_Smb2SessionTracker -{ -public: - - DCE2_Smb2SessionTracker(); - ~DCE2_Smb2SessionTracker(); - - void removeSessionFromAllConnection(); - - // tree tracker - bool insertTtracker(uint32_t tree_id, DCE2_Smb2TreeTracker* ttr) - { - return 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) - { - tree_trackers.Remove(tree_id); - } - - // ssd tracker - bool insertConnTracker(SmbFlowKey key, DCE2_Smb2SsnData* ssd) - { - return conn_trackers.Insert(key, ssd); - } - - DCE2_Smb2SsnData* findConnTracker(SmbFlowKey key) - { - return conn_trackers.Find(key); - } - - void removeConnTracker(SmbFlowKey key) - { - conn_trackers.Remove(key); - } - - int getConnTrackerSize() - { - return conn_trackers.GetSize(); - } - - 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->getRtrackerSize(); - } - return total_count; - } - - void set_session_id(uint64_t sid) - { - session_id = sid; - conn_trackers.SetDoNotFree(); - } - - DCE2_DbMapConntracker conn_trackers; - DCE2_DbMapTtracker tree_trackers; - Smb2SidHashKey session_key; - uint64_t session_id = 0; -}; - -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 - SmbFlowKey flow_key; -}; - -/* SMB2 command codes */ -#define SMB2_COM_NEGOTIATE 0x00 -#define SMB2_COM_SESSION_SETUP 0x01 -#define SMB2_COM_LOGOFF 0x02 -#define SMB2_COM_TREE_CONNECT 0x03 -#define SMB2_COM_TREE_DISCONNECT 0x04 -#define SMB2_COM_CREATE 0x05 -#define SMB2_COM_CLOSE 0x06 -#define SMB2_COM_FLUSH 0x07 -#define SMB2_COM_READ 0x08 -#define SMB2_COM_WRITE 0x09 -#define SMB2_COM_LOCK 0x0A -#define SMB2_COM_IOCTL 0x0B -#define SMB2_COM_CANCEL 0x0C -#define SMB2_COM_ECHO 0x0D -#define SMB2_COM_QUERY_DIRECTORY 0x0E -#define SMB2_COM_CHANGE_NOTIFY 0x0F -#define SMB2_COM_QUERY_INFO 0x10 -#define SMB2_COM_SET_INFO 0x11 -#define SMB2_COM_OPLOCK_BREAK 0x12 -#define SMB2_COM_MAX 0x13 - struct Smb2WriteRequestHdr { uint16_t structure_size; /* This MUST be set to 49 */ @@ -549,9 +184,6 @@ struct Smb2CreateRequestHdr uint32_t create_contexts_length; /* length of contexts */ }; -// file attribute for create response -#define SMB2_CREATE_RESPONSE_DIRECTORY 0x10 - struct Smb2CreateResponseHdr { uint16_t structure_size; /* This MUST be set to 89 */ @@ -572,6 +204,16 @@ struct Smb2CreateResponseHdr uint32_t create_contexts_length; /* */ }; +struct Smb2CreateContextHdr +{ + uint32_t next; /* next context header*/ + uint16_t name_offset; /* name offset */ + uint16_t name_length; /* name length */ + uint16_t reserved; /* reserved */ + uint16_t data_offset; /* data offset */ + uint32_t data_length; /* data length */ +}; + struct Smb2CloseRequestHdr { uint16_t structure_size; /* This MUST be set to 24 */ @@ -581,10 +223,6 @@ struct Smb2CloseRequestHdr uint64_t fileId_volatile; /* fileId that is volatile */ }; -#define SMB2_SHARE_TYPE_DISK 0x01 -#define SMB2_SHARE_TYPE_PIPE 0x02 -#define SMB2_SHARE_TYPE_PRINT 0x03 - struct Smb2TreeConnectResponseHdr { uint16_t structure_size; /* This MUST be set to 16 */ @@ -595,39 +233,12 @@ struct Smb2TreeConnectResponseHdr uint32_t maximal_access; /* maximal access for the user */ }; -struct Smb2TreeDisConnectHdr -{ - uint16_t structure_size; /* This MUST be set to 4 */ - 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 #define SMB2_CREATE_REQUEST_STRUC_SIZE 57 #define SMB2_CREATE_RESPONSE_STRUC_SIZE 89 -#define SMB2_CREATE_REQUEST_DATA_OFFSET 120 #define SMB2_CLOSE_REQUEST_STRUC_SIZE 24 #define SMB2_CLOSE_RESPONSE_STRUC_SIZE 60 @@ -644,6 +255,7 @@ struct Smb2SetupResponseHdr #define SMB2_TREE_CONNECT_REQUEST_STRUC_SIZE 9 #define SMB2_TREE_CONNECT_RESPONSE_STRUC_SIZE 16 #define SMB2_TREE_DISCONNECT_REQUEST_STRUC_SIZE 4 +#define SMB2_TREE_DISCONNECT_RESPONSE_STRUC_SIZE 4 #define SMB2_FILE_ENDOFFILE_INFO 0x14 @@ -651,13 +263,176 @@ struct Smb2SetupResponseHdr #define SMB2_SETUP_RESPONSE_STRUC_SIZE 9 #define SMB2_LOGOFF_REQUEST_STRUC_SIZE 4 +#define SMB2_LOGOFF_RESPONSE_STRUC_SIZE 4 -extern const char* smb2_command_string[SMB2_COM_MAX]; -/* Process smb2 message */ -void DCE2_Smb2Process(DCE2_Smb2SsnData* ssd); +#define GET_CURRENT_PACKET snort::DetectionEngine::get_current_packet() + +class Dce2Smb2FileTracker; +class Dce2Smb2SessionTracker; + +using Dce2Smb2SessionTrackerMap = + std::unordered_map >; + +PADDING_GUARD_BEGIN +struct Smb2SessionKey +{ + uint32_t cip[4]; + uint32_t sip[4]; + uint64_t sid; + int16_t cgroup; + int16_t sgroup; + uint16_t asid; + uint16_t padding; + + bool operator==(const Smb2SessionKey& other) const + { + return( sid == other.sid and + cip[0] == other.cip[0] and + cip[1] == other.cip[1] and + cip[2] == other.cip[2] and + cip[3] == other.cip[3] and + sip[0] == other.sip[0] and + sip[1] == other.sip[1] and + sip[2] == other.sip[2] and + sip[3] == other.sip[3] and + cgroup == other.cgroup and + sgroup == other.sgroup and + asid == other.asid ); + } +}; + +struct Smb2FlowKey +{ + uint32_t ip_l[4]; // Low IP + uint32_t ip_h[4]; // High IP + uint32_t mplsLabel; + uint16_t port_l; // Low Port - 0 if ICMP + uint16_t port_h; // High Port - 0 if ICMP + int16_t group_l; + int16_t group_h; + uint16_t vlan_tag; + uint16_t addressSpaceId; + uint8_t ip_protocol; + uint8_t pkt_type; + uint8_t version; + uint8_t padding; + + bool operator==(const Smb2FlowKey& other) const + { + return (ip_l[0] == other.ip_l[0] and + ip_l[1] == other.ip_l[1] and + ip_l[2] == other.ip_l[2] and + ip_l[3] == other.ip_l[3] and + ip_h[0] == other.ip_h[0] and + ip_l[1] == other.ip_l[1] and + ip_l[2] == other.ip_l[2] and + ip_l[3] == other.ip_l[3] and + mplsLabel == other.mplsLabel and + port_l == other.port_l and + port_h == other.port_h and + group_l == other.group_l and + group_h == other.group_h and + vlan_tag == other.vlan_tag and + addressSpaceId == other.addressSpaceId and + ip_protocol == other.ip_protocol and + pkt_type == other.pkt_type and + version == other.version); + } +}; +PADDING_GUARD_END + +//The below value is taken from Hash Key class static hash hardener +#define SMB_KEY_HASH_HARDENER 133824503 + +struct Smb2KeyHash +{ + size_t operator()(const Smb2FlowKey& key) const + { + return do_hash_flow_key((const uint32_t*)&key); + } + + size_t operator()(const Smb2SessionKey& key) const + { + return do_hash_session_key((const uint32_t*)&key); + } + +private: + size_t do_hash_flow_key(const uint32_t* d) const + { + uint32_t a, b, c; + a = b = c = SMB_KEY_HASH_HARDENER; + a += d[0]; b += d[1]; c += d[2]; mix(a, b, c); + a += d[3]; b += d[4]; c += d[5]; mix(a, b, c); + a += d[6]; b += d[7]; c += d[8]; mix(a, b, c); + a += d[9]; b += d[10]; c += d[11]; mix(a, b, c); + a += d[12]; finalize(a, b, c); + return c; + } + + size_t do_hash_session_key(const uint32_t* d) const + { + uint32_t a, b, c; + a = b = c = SMB_KEY_HASH_HARDENER; + a += d[0]; b += d[1]; c += d[2]; mix(a, b, c); + a += d[3]; b += d[4]; c += d[5]; mix(a, b, c); + a += d[6]; b += d[7]; c += d[8]; mix(a, b, c); + a += d[9]; b += d[10]; c += d[11]; finalize(a, b, c); + return c; + } + + inline uint32_t rot(uint32_t x, unsigned k) const + { return (x << k) | (x >> (32 - k)); } + + inline void mix(uint32_t& a, uint32_t& b, uint32_t& c) const + { + a -= c; a ^= rot(c, 4); c += b; + b -= a; b ^= rot(a, 6); a += c; + c -= b; c ^= rot(b, 8); b += a; + a -= c; a ^= rot(c,16); c += b; + b -= a; b ^= rot(a,19); a += c; + c -= b; c ^= rot(b, 4); b += a; + } + + inline void finalize(uint32_t& a, uint32_t& b, uint32_t& c) const + { + c ^= b; c -= rot(b,14); + a ^= c; a -= rot(c,11); + b ^= a; b -= rot(a,25); + c ^= b; c -= rot(b,16); + a ^= c; a -= rot(c,4); + b ^= a; b -= rot(a,14); + c ^= b; c -= rot(b,24); + } +}; + +Smb2FlowKey get_smb2_flow_key(void); + +class Dce2Smb2SessionData : public Dce2SmbSessionData +{ +public: + Dce2Smb2SessionData() = delete; + Dce2Smb2SessionData(const snort::Packet*, const dce2SmbProtoConf* proto); + ~Dce2Smb2SessionData() override; + void process() override; + void remove_session(uint64_t); + void handle_retransmit(FilePosition, FileVerdict) override { } + void reset_matching_tcp_file_tracker(Dce2Smb2FileTracker*); + void set_tcp_file_tracker(Dce2Smb2FileTracker* file_tracker) + { tcp_file_tracker = file_tracker; } + +private: + void process_command(const Smb2Hdr*, const uint8_t*); + Smb2SessionKey get_session_key(uint64_t); + Dce2Smb2SessionTracker* create_session(uint64_t); + Dce2Smb2SessionTracker* find_session(uint64_t); + + Smb2FlowKey flow_key; + Dce2Smb2FileTracker* tcp_file_tracker; + Dce2Smb2SessionTrackerMap connected_sessions; +}; -/* Check smb version based on smb header */ -DCE2_SmbVersion DCE2_Smb2Version(const snort::Packet* p); +using Dce2Smb2SessionDataMap = + std::unordered_map; #endif /* _DCE_SMB2_H_ */ diff --git a/src/service_inspectors/dce_rpc/dce_smb2_commands.cc b/src/service_inspectors/dce_rpc/dce_smb2_commands.cc deleted file mode 100644 index 50b60d17b..000000000 --- a/src/service_inspectors/dce_rpc/dce_smb2_commands.cc +++ /dev/null @@ -1,869 +0,0 @@ -//-------------------------------------------------------------------------- -// 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 "hash/hash_key_operations.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, cmd) \ - { \ - if ((smb_data + (strcuture_size)) > end) \ - { \ - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), \ - "%s : smb data beyond end detected\n", smb2_command_string[cmd]); \ - counter ++; \ - return; \ - } \ - } - -static inline FileContext* get_smb_file_context(uint64_t file_id, uint64_t - multi_file_processing_id, - bool to_create = false) -{ - 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, to_create, multi_file_processing_id); -} - -static void DCE2_Smb2CleanFtrackerTcpRef(DCE2_Smb2SessionTracker* str, uint64_t file_id) -{ - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "updating conn for fid %" PRIu64 "\n", file_id); - auto all_conn_trackers = str->conn_trackers.get_all_entry(); - for ( auto& h : all_conn_trackers ) - { - if (h.second->ftracker_tcp) - { - if (h.second->ftracker_tcp->file_id == file_id) - { - h.second->ftracker_tcp = nullptr; - } - } - } -} - -DCE2_Smb2TreeTracker *find_tree_for_message(DCE2_Smb2SessionTracker *str, const uint64_t mid) -{ - auto all_tree_trackers = str->tree_trackers.get_all_entry(); - for ( auto& h : all_tree_trackers ) - { - if(h.second->findRtracker(mid)) - return h.second; - } - return nullptr; -} - -bool DCE2_Smb2ProcessFileData(DCE2_Smb2SsnData* ssd, const uint8_t* file_data, - uint32_t data_size) -{ - 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; - FileDirection dir = ssd->ftracker_tcp->upload ? FILE_UPLOAD : FILE_DOWNLOAD; - - debug_logf(dce_smb_trace, p, "file_process fid %" PRIu64 " data_size %" PRIu32 "" - " offset %" PRIu64 " bytes processed %" PRIu64 "\n", ssd->ftracker_tcp->file_id, - data_size, ssd->ftracker_tcp->file_offset, ssd->ftracker_tcp->bytes_processed); - - // 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++; - debug_logf(dce_smb_trace, p, "extra file data\n"); - - DCE2_Smb2TreeTracker* ttr = ssd->ftracker_tcp->ttr; - uint64_t file_id = ssd->ftracker_tcp->file_id; - DCE2_Smb2CleanFtrackerTcpRef(ssd->ftracker_tcp->str, file_id); - ttr->removeFtracker(file_id); - - return false; - } - - if (!file_flows->file_process(p, ssd->ftracker_tcp->file_name_hash, file_data, data_size, - ssd->ftracker_tcp->file_offset, dir, ssd->ftracker_tcp->file_id) and detection_size) - { - debug_logf(dce_smb_trace, p, "file_process completed\n"); - - DCE2_Smb2TreeTracker* ttr = ssd->ftracker_tcp->ttr; - uint64_t file_id = ssd->ftracker_tcp->file_id; - DCE2_Smb2CleanFtrackerTcpRef(ssd->ftracker_tcp->str, file_id); - ttr->removeFtracker(file_id); - - return false; - } - return true; -} - -//------------------------------------------------------------------------- -// 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)) - { - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s_RESP: error\n", smb2_command_string[SMB2_COM_SESSION_SETUP]); - 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, SMB2_COM_SESSION_SETUP) - DCE2_Smb2FindElseCreateSid(ssd, sid); - } - else if (structure_size != SMB2_SETUP_REQUEST_STRUC_SIZE) - { - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s: invalid struct size\n", smb2_command_string[SMB2_COM_SESSION_SETUP]); - 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, SMB2_COM_TREE_CONNECT) - - if (!DCE2_Smb2InsertTid(ssd, tid, - ((const Smb2TreeConnectResponseHdr*)smb_data)->share_type, str)) - { - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s: ignored %d\n", smb2_command_string[SMB2_COM_TREE_CONNECT], tid); - dce2_smb_stats.v2_tree_cnct_ignored++; - } - } - else if (structure_size != SMB2_TREE_CONNECT_REQUEST_STRUC_SIZE) - { - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s: invalid struct size\n", smb2_command_string[SMB2_COM_TREE_CONNECT]); - 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, SMB2_COM_TREE_DISCONNECT) - str->removeTtracker(tid); - } - else - { - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s: invalid struct size\n", smb2_command_string[SMB2_COM_TREE_DISCONNECT]); - 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++; - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s_REQ: invalid file data seen\n", smb2_command_string[SMB2_COM_CREATE]); - 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++; - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s_REQ: invalid file data seen with size %" PRIu16 "\n", - smb2_command_string[SMB2_COM_CREATE], size); - - return; - } - - if (ssd->max_outstanding_requests > str->getTotalRequestsPending()) - { - DCE2_Smb2RequestTracker* rtracker = ttr->findRtracker(mid); - if (rtracker) // Cleanup existing tracker - ttr->removeRtracker(mid); - - char* file_name = DCE2_SmbGetFileName(file_data, size, true, &name_len); - - rtracker = new DCE2_Smb2RequestTracker(file_name, name_len); - ttr->insertRtracker(mid, rtracker); - } - else - { - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s_REQ: max req exceeded\n", smb2_command_string[SMB2_COM_CREATE]); - dce_alert(GID_DCE2, DCE2_SMB_MAX_REQS_EXCEEDED, (dce2CommonStats*)&dce2_smb_stats, - ssd->sd); - } - } - else - { - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s_REQ: name_offset %" PRIu16 "\n", smb2_command_string[SMB2_COM_CREATE], name_offset); - 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, - DCE2_Smb2TreeTracker* ttr, DCE2_Smb2SessionTracker* str, uint64_t fileId_persistent) -{ - uint64_t file_size = 0; - - if (smb_create_hdr->end_of_file) - { - file_size = alignedNtohq((const uint64_t*)(&(smb_create_hdr->end_of_file))); - } - - DCE2_Smb2FileTracker* ftracker = ttr->findFtracker(fileId_persistent); - if (!ftracker) - { - ftracker = new DCE2_Smb2FileTracker(fileId_persistent, ttr, str, DetectionEngine::get_current_packet()->flow); - ttr->insertFtracker(fileId_persistent, ftracker); - } - ftracker->file_name = rtracker->fname; - ftracker->file_name_len = rtracker->fname_len; - ftracker->file_size = file_size; - - if (rtracker->fname and rtracker->fname_len) - { - ftracker->file_name_hash = str_to_hash( - (const uint8_t*)rtracker->fname, rtracker->fname_len); - - FileContext* file = get_smb_file_context(ftracker->file_name_hash, fileId_persistent, - true); - - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s_RESP: file size %" PRIu64 " fid %" PRIu64 "" - "file_name_hash %" PRIu64 " file context %s\n", smb2_command_string[SMB2_COM_CREATE], - file_size, fileId_persistent, ftracker->file_name_hash, (file ? "found" : "not found")); - - if (file) - { - if (file->verdict == FILE_VERDICT_UNKNOWN) - { - file->set_file_size(!file_size ? UNKNOWN_FILE_SIZE : file_size); - file->set_file_name(ftracker->file_name, ftracker->file_name_len); - } - } - else - { - ftracker->ignore = true; // could not create file context, hence this file transfer - // cant be inspected - } - rtracker->set_file_id(fileId_persistent); // to ensure file tracker will free file name - } - else - { - ftracker->ignore = true; // file can not be inspected as file name is null - } -} - -//------------------------------------------------------------------------- -// 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); - DCE2_Smb2TreeTracker* ttr = str->findTtracker(tid); - uint16_t structure_size = alignedNtohs((const uint16_t*)smb_data); - - if (structure_size == SMB2_ERROR_RESPONSE_STRUC_SIZE and Smb2Error(smb_hdr)) - { - if (ttr) - ttr->removeRtracker(mid); - - dce2_smb_stats.v2_crt_err_resp++; - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s_RESP: error\n", smb2_command_string[SMB2_COM_CREATE]); - } - // 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, SMB2_COM_CREATE) - - if (!ttr) - { - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s_REQ: mid stream session detected\n", smb2_command_string[SMB2_COM_CREATE]); - ttr = DCE2_Smb2InsertTid(ssd, tid, SMB2_SHARE_TYPE_DISK, str); - if (!ttr) - { - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s_REQ: insert tree tracker failed\n", smb2_command_string[SMB2_COM_CREATE]); - return; - } - } - else if (SMB2_SHARE_TYPE_DISK != ttr->get_share_type()) - { - dce2_smb_stats.v2_crt_req_ipc++; - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s_REQ: ignored for ipc share\n", smb2_command_string[SMB2_COM_CREATE]); - 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, SMB2_COM_CREATE) - - if(!tid) - ttr = find_tree_for_message(str, mid); - if (!ttr) - { - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s_RESP: tree tracker missing\n", smb2_command_string[SMB2_COM_CREATE]); - dce2_smb_stats.v2_crt_tree_trkr_misng++; - return; - } - - DCE2_Smb2RequestTracker* rtr = ttr->findRtracker(mid); - if (!rtr) - { - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s_RESP: req tracker missing\n", smb2_command_string[SMB2_COM_CREATE]); - dce2_smb_stats.v2_crt_rtrkr_misng++; - return; - } - - uint64_t fileId_persistent = alignedNtohq((const uint64_t*)( - &(((const Smb2CreateResponseHdr*)smb_data)->fileId_persistent))); - - if (((const Smb2CreateResponseHdr*)smb_data)->file_attributes & - SMB2_CREATE_RESPONSE_DIRECTORY) - { - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s_RESP: not processing for directory\n", smb2_command_string[SMB2_COM_CREATE]); - ttr->removeRtracker(mid); - DCE2_Smb2CleanFtrackerTcpRef(str, fileId_persistent); - ttr->removeFtracker(fileId_persistent); - return; - } - - DCE2_Smb2CreateResponse(ssd, (const Smb2CreateResponseHdr*)smb_data, rtr, ttr, - str, fileId_persistent); - ttr->removeRtracker(mid); - } - else - { - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s: invalid struct size\n", smb2_command_string[SMB2_COM_CREATE]); - 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, - DCE2_Smb2SessionTracker* str) -{ - uint16_t structure_size = alignedNtohs((const uint16_t*)smb_data); - - if (structure_size == SMB2_ERROR_RESPONSE_STRUC_SIZE and Smb2Error(smb_hdr)) - { - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s_RESP: error\n", smb2_command_string[SMB2_COM_CLOSE]); - 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, SMB2_COM_CLOSE) - - 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++; - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s_REQ: ftracker missing %" PRIu64 "\n", - smb2_command_string[SMB2_COM_CLOSE], fileId_persistent); - return; - } - - if (!ftracker->ignore and !ftracker->file_size and ftracker->file_offset) - { - ftracker->file_size = ftracker->file_offset; - FileContext* file = get_smb_file_context(ftracker->file_name_hash, fileId_persistent); - if (file) - { - file->set_file_size(ftracker->file_size); - } - - ssd->ftracker_tcp = ftracker; - - // 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); - } - else - { - DCE2_Smb2CleanFtrackerTcpRef(str, fileId_persistent); - ttr->removeFtracker(fileId_persistent); - } - } - else if (structure_size != SMB2_CLOSE_RESPONSE_STRUC_SIZE) - { - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s: invalid struct size\n", smb2_command_string[SMB2_COM_CLOSE]); - 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)) - { - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s: error resp\n", smb2_command_string[SMB2_COM_SET_INFO]); - 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, SMB2_COM_SET_INFO) - - 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 and !ftracker->ignore) - { - ftracker->file_size = file_size; - FileContext* file = get_smb_file_context(ftracker->file_name_hash, - fileId_persistent); - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s_REQ: set file size %" PRIu64 " fid %" PRIu64 " file context %s\n", - smb2_command_string[SMB2_COM_SET_INFO], file_size, fileId_persistent, - file ? "found" : "not found"); - if (file) - { - file->set_file_size(ftracker->file_size); - } - } - else - { - dce2_smb_stats.v2_stinf_req_ftrkr_misng++; - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s_REQ: ftracker missing\n", smb2_command_string[SMB2_COM_SET_INFO]); - } - } - else - { - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s_REQ: header error\n", smb2_command_string[SMB2_COM_SET_INFO]); - dce2_smb_stats.v2_stinf_req_hdr_err++; - } - } - else if (structure_size != SMB2_SET_INFO_RESPONSE_STRUC_SIZE) - { - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s: invalid struct size\n", smb2_command_string[SMB2_COM_SET_INFO]); - 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) -{ - 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()) - { - DCE2_Smb2RequestTracker* readtracker = ttr->findRtracker(message_id); - if (!readtracker) - { - readtracker = new DCE2_Smb2RequestTracker(fileId_persistent, offset); - ttr->insertRtracker(message_id, readtracker); - } - } - else - { - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s_REQ: max req exceeded\n", smb2_command_string[SMB2_COM_READ]); - 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, ttr, str, DetectionEngine::get_current_packet()->flow); - ttr->insertFtracker(fileId_persistent, ftracker); - } - - if (ftracker->file_size and (offset > ftracker->file_size)) - { - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s_REQ: invalid file offset\n", smb2_command_string[SMB2_COM_READ]); - 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->findRtracker(message_id); - if (!request) - { - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s_RESP: request tracker missing\n", smb2_command_string[SMB2_COM_READ]); - 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) - { - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s_RESP: bad offset\n", smb2_command_string[SMB2_COM_READ]); - 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 and !ftracker->ignore ) - { - ftracker->file_offset = request->get_offset(); - ttr->removeRtracker(message_id); - - ssd->ftracker_tcp = ftracker; - - if (!DCE2_Smb2ProcessFileData(ssd, file_data, data_size)) - return; - ftracker->file_offset += data_size; - - uint32_t total_data_length = alignedNtohl((const uint32_t*)&(smb_read_hdr->length)); - 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->findRtracker(mid); - if (rtr and rtr->get_file_id()) - { - DCE2_Smb2CleanFtrackerTcpRef(str, rtr->get_file_id()); - ttr->removeFtracker(rtr->get_file_id()); - } - ttr->removeRtracker(mid); - dce2_smb_stats.v2_read_err_resp++; - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s_RESP: error\n", smb2_command_string[SMB2_COM_WRITE]); - } - // 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, SMB2_COM_READ) - 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, SMB2_COM_READ) - - DCE2_Smb2ReadResponse(ssd, smb_hdr, (const Smb2ReadResponseHdr*)smb_data, end, ttr, mid); - } - else - { - dce2_smb_stats.v2_read_inv_str_sz++; - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s: invalid struct size\n", smb2_command_string[SMB2_COM_WRITE]); - } -} - -//------------------------------------------------------------------------- -// 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; - - fileId_persistent = alignedNtohq((const uint64_t*)(&(smb_write_hdr->fileId_persistent))); - - if (ssd->max_outstanding_requests > str->getTotalRequestsPending()) - { - DCE2_Smb2RequestTracker* writetracker = ttr->findRtracker(mid); - if (!writetracker) - { - writetracker = new DCE2_Smb2RequestTracker(fileId_persistent); - ttr->insertRtracker(mid, writetracker); - } - } - else - { - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s_REQ: max req exceeded\n", smb2_command_string[SMB2_COM_WRITE]); - 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) - { - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s_REQ: bad offset\n", smb2_command_string[SMB2_COM_WRITE]); - 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) // compounded create request + write request case - { - ftracker = new DCE2_Smb2FileTracker(fileId_persistent, ttr, str, DetectionEngine::get_current_packet()->flow); - ttr->insertFtracker(fileId_persistent, ftracker); - } - if (!ftracker->ignore) // file tracker can not be nullptr 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; - ftracker->upload = true; - - ssd->ftracker_tcp = ftracker; - - if (!DCE2_Smb2ProcessFileData(ssd, file_data, data_size)) - return; - ftracker->file_offset += data_size; - uint32_t total_data_length = alignedNtohl((const uint32_t*)&(smb_write_hdr->length)); - 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->findRtracker(mid); - if (wtr and wtr->get_file_id()) - { - DCE2_Smb2CleanFtrackerTcpRef(str, wtr->get_file_id()); - ttr->removeFtracker(wtr->get_file_id()); - } - ttr->removeRtracker(mid); - dce2_smb_stats.v2_wrt_err_resp++; - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s_RESP: error\n", smb2_command_string[SMB2_COM_WRITE]); - } - // 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, SMB2_COM_WRITE) - DCE2_Smb2WriteRequest(ssd, smb_hdr, (const Smb2WriteRequestHdr*)smb_data, end, str, ttr, - mid); - } - else if (structure_size == SMB2_WRITE_RESPONSE_STRUC_SIZE) - { - ttr->removeRtracker(mid); - } - else - { - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s: invalid struct size\n", smb2_command_string[SMB2_COM_WRITE]); - 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_Smb2SessionTracker* str = DCE2_Smb2FindSidInSsd(ssd, sid); - if (str) - { - str->removeSessionFromAllConnection(); - DCE2_SmbSessionCacheRemove(str->session_key); - } - } - else - { - debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), - "%s: invalid struct size\n", smb2_command_string[SMB2_COM_LOGOFF]); - 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 deleted file mode 100644 index 22c93a973..000000000 --- a/src/service_inspectors/dce_rpc/dce_smb2_commands.h +++ /dev/null @@ -1,68 +0,0 @@ -//-------------------------------------------------------------------------- -// 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); - -bool DCE2_Smb2ProcessFileData(DCE2_Smb2SsnData*, const uint8_t* file_data, - uint32_t data_size); - -void DCE2_Smb2CloseCmd(DCE2_Smb2SsnData*, const Smb2Hdr*, - const uint8_t* smb_data, const uint8_t* end, DCE2_Smb2TreeTracker* ttr, - DCE2_Smb2SessionTracker* str); - -void DCE2_Smb2Logoff(DCE2_Smb2SsnData*, const uint8_t* smb_data, - const uint64_t sid); - -DCE2_Smb2TreeTracker *find_tree_for_message(DCE2_Smb2SessionTracker*, const uint64_t); - -#endif - diff --git a/src/service_inspectors/dce_rpc/dce_smb2_file.cc b/src/service_inspectors/dce_rpc/dce_smb2_file.cc new file mode 100644 index 000000000..9e18e32e8 --- /dev/null +++ b/src/service_inspectors/dce_rpc/dce_smb2_file.cc @@ -0,0 +1,176 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2021 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_file.cc author Dipta Pandit + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "dce_smb2_file.h" + +#include "file_api/file_flows.h" +#include "hash/hash_key_operations.h" + +#include "dce_smb2_session.h" +#include "dce_smb2_tree.h" + +using namespace snort; + +#define UNKNOWN_FILE_SIZE (~0) + +void Dce2Smb2FileTracker::accept_raw_data_from(Dce2Smb2SessionData* flow) +{ + if (flow) + { + smb2_pdu_state = DCE2_SMB_PDU_STATE__RAW_DATA; + flow->set_tcp_file_tracker(this); + } +} + +inline void Dce2Smb2FileTracker::file_detect() +{ + DetectionEngine::detect(DetectionEngine::get_current_packet()); + dce2_detected = 1; +} + +void Dce2Smb2FileTracker::set_info(char* file_name_v, uint16_t name_len_v, + uint64_t size_v, bool create) +{ + if (file_name_v and name_len_v) + { + file_name = file_name_v; + file_name_len = name_len_v; + file_name_hash = str_to_hash((uint8_t*)file_name, file_name_len); + } + file_size = size_v; + FileContext* file = get_smb_file_context(file_name_hash, file_id, create); + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, "set file info: file size %" + PRIu64 " fid %" PRIu64 " file_name_hash %" PRIu64 " file context " + "%sfound\n", file_size, file_id, file_name_hash, (file ? "" : "not ")); + if (file) + { + ignore = false; + if (file->verdict == FILE_VERDICT_UNKNOWN) + { + if (file_name_v and name_len_v) + file->set_file_name(file_name, file_name_len); + file->set_file_size(file_size ? file_size : UNKNOWN_FILE_SIZE); + } + } +} + +bool Dce2Smb2FileTracker::close() +{ + if (!ignore and !file_size and file_offset) + { + file_size = file_offset; + FileContext* file = + get_smb_file_context(file_name_hash, file_id, false); + if (file) + file->set_file_size(file_size); + return (!process_data(nullptr, 0)); + } + return true; +} + +bool Dce2Smb2FileTracker::process_data(const uint8_t* file_data, + uint32_t data_size, uint64_t offset) +{ + file_offset = offset; + return process_data(file_data, data_size); +} + +bool Dce2Smb2FileTracker::process_data(const uint8_t* file_data, + uint32_t data_size) +{ + Dce2Smb2SessionData* current_flow = parent_tree->get_parent()->get_current_flow(); + int64_t file_detection_depth = current_flow->get_smb_file_depth(); + int64_t detection_size = 0; + + if (file_detection_depth == 0) + detection_size = data_size; + else if ( file_offset < (uint64_t)file_detection_depth) + { + if ( file_detection_depth - file_offset < data_size ) + detection_size = file_detection_depth - 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); + file_detect(); + } + + if (ignore) + return true; + + Packet* p = DetectionEngine::get_current_packet(); + + if (file_size and file_offset > file_size) + { + debug_logf(dce_smb_trace, p, "file_process: bad offset\n"); + dce_alert(GID_DCE2, DCE2_SMB_INVALID_FILE_OFFSET, (dce2CommonStats*) + &dce2_smb_stats, *(current_flow->get_dce2_session_data())); + } + + debug_logf(dce_smb_trace, p, "file_process fid %" PRIu64 " data_size %" + PRIu32 " offset %" PRIu64 "\n", file_id, data_size, file_offset); + + FileFlows* file_flows = FileFlows::get_file_flows(p->flow); + + if (!file_flows) + return true; + + if (!file_flows->file_process(p, file_name_hash, file_data, data_size, + file_offset, direction, file_id)) + { + debug_logf(dce_smb_trace, p, "file_process completed\n"); + return false; + } + + file_offset += data_size; + return true; +} + +Dce2Smb2FileTracker::~Dce2Smb2FileTracker(void) +{ + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, "file tracker %" PRIu64 + " file name hash %" PRIu64 " terminating\n", file_id, file_name_hash); + + if (file_name) + snort_free((void*)file_name); + + Dce2Smb2SessionDataMap attached_flows = parent_tree->get_parent()->get_attached_flows(); + + for (auto it_flow : attached_flows) + { + FileFlows* file_flows = FileFlows::get_file_flows(it_flow.second->get_flow(), false); + if (file_flows) + file_flows->remove_processed_file_context(file_name_hash, file_id); + it_flow.second->reset_matching_tcp_file_tracker(this); + } + + parent_tree->close_file(file_id, false); + + memory::MemoryCap::update_deallocations(sizeof(*this)); +} + diff --git a/src/service_inspectors/dce_rpc/dce_smb2_file.h b/src/service_inspectors/dce_rpc/dce_smb2_file.h new file mode 100644 index 000000000..0384c7a9b --- /dev/null +++ b/src/service_inspectors/dce_rpc/dce_smb2_file.h @@ -0,0 +1,80 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2021 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_file.h author Dipta Pandit + +#ifndef DCE_SMB2_FILE_H +#define DCE_SMB2_FILE_H + +// This provides file tracker for SMBv2 + +#include "dce_smb2.h" + +class Dce2Smb2TreeTracker; + +class Dce2Smb2FileTracker +{ +public: + + Dce2Smb2FileTracker() = delete; + Dce2Smb2FileTracker(const Dce2Smb2FileTracker& arg) = delete; + Dce2Smb2FileTracker& operator=(const Dce2Smb2FileTracker& arg) = delete; + + Dce2Smb2FileTracker(uint64_t file_idv, Dce2Smb2TreeTracker* p_tree) : ignore(true), + file_name_len(0), file_offset(0), file_id(file_idv), file_size(0), file_name_hash(0), + file_name(nullptr), direction(FILE_DOWNLOAD), smb2_pdu_state(DCE2_SMB_PDU_STATE__COMMAND), + parent_tree(p_tree) + { + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, + "file tracker %" PRIu64 " created\n", file_id); + memory::MemoryCap::update_allocations(sizeof(*this)); + } + + ~Dce2Smb2FileTracker(); + bool process_data(const uint8_t*, uint32_t, uint64_t); + bool process_data(const uint8_t*, uint32_t); + bool close(); + void set_info(char*, uint16_t, uint64_t, bool = false); + void accept_raw_data_from(Dce2Smb2SessionData*); + + bool accepting_raw_data() + { return (smb2_pdu_state == DCE2_SMB_PDU_STATE__RAW_DATA); } + + void set_direction(FileDirection dir) { direction = dir; } + Dce2Smb2TreeTracker* get_parent() { return parent_tree; } + uint64_t get_file_id() { return file_id; } + +private: + void file_detect(); + bool ignore; + uint16_t file_name_len; + uint64_t file_offset; + uint64_t file_id; + uint64_t file_size; + uint64_t file_name_hash; + char* file_name; + FileDirection direction; + Dce2SmbPduState smb2_pdu_state; + Dce2Smb2TreeTracker* parent_tree; +}; + +using Dce2Smb2FileTrackerMap = + std::unordered_map >; + +#endif + diff --git a/src/service_inspectors/dce_rpc/dce_smb2_request.h b/src/service_inspectors/dce_rpc/dce_smb2_request.h new file mode 100644 index 000000000..b78230f69 --- /dev/null +++ b/src/service_inspectors/dce_rpc/dce_smb2_request.h @@ -0,0 +1,76 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2021 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_request.h author Bhargava Jandhyala + +#ifndef DCE_SMB2_REQUEST_H +#define DCE_SMB2_REQUEST_H + +// This provides request trackers for SMBv2. +// Request trackers are used to track CREATE, READ and WRITE requests + +#include "dce_smb2.h" + +class Dce2Smb2RequestTracker +{ +public: + + Dce2Smb2RequestTracker() = delete; + Dce2Smb2RequestTracker(const Dce2Smb2RequestTracker& arg) = delete; + Dce2Smb2RequestTracker& operator=(const Dce2Smb2RequestTracker& arg) = delete; + + Dce2Smb2RequestTracker(uint64_t file_id_v, uint64_t offset_v = 0) + : fname(nullptr), fname_len(0), file_id(file_id_v), offset(offset_v) + { + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, "request tracker created\n"); + memory::MemoryCap::update_allocations(sizeof(*this)); + } + + Dce2Smb2RequestTracker(char* fname_v, uint16_t fname_len_v) + : fname(fname_v), fname_len(fname_len_v), file_id(0), offset(0) + { + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, "request tracker created\n"); + memory::MemoryCap::update_allocations(sizeof(*this)); + } + + ~Dce2Smb2RequestTracker() + { + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, "request tracker terminating\n"); + if (fname) + snort_free(fname); + memory::MemoryCap::update_deallocations(sizeof(*this)); + } + + void reset_file_name() { fname = nullptr; fname_len = 0; } + uint64_t get_offset() { return offset; } + uint64_t get_file_id() { return file_id; } + char* get_file_name() { return fname; } + uint16_t get_file_name_size() { return fname_len; } + +private: + char* fname; + uint16_t fname_len; + uint64_t file_id; + uint64_t offset; +}; + +using Dce2Smb2RequestTrackerMap = + std::unordered_map >; + +#endif + diff --git a/src/service_inspectors/dce_rpc/dce_smb2_session.cc b/src/service_inspectors/dce_rpc/dce_smb2_session.cc new file mode 100644 index 000000000..deb12aa51 --- /dev/null +++ b/src/service_inspectors/dce_rpc/dce_smb2_session.cc @@ -0,0 +1,180 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2021 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_session.cc author Dipta Pandit + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "dce_smb2_session.h" + +inline uint32_t Smb2Tid(const Smb2Hdr* hdr) +{ + return snort::alignedNtohl(&(((const Smb2SyncHdr*)hdr)->tree_id)); +} + +//init must be called when a session tracker is created. +void Dce2Smb2SessionTracker::init(uint64_t sid, + const Smb2SessionKey& session_key_v) +{ + session_id = sid; + session_key = session_key_v; + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, "session tracker %" PRIu64 + " created\n", session_id); +} + +Dce2Smb2SessionData* Dce2Smb2SessionTracker::get_current_flow() +{ + Smb2FlowKey flow_key = get_smb2_flow_key(); + auto it_flow = attached_flows.find(flow_key); + return (it_flow != attached_flows.end()) ? it_flow->second : nullptr; +} + +Dce2Smb2TreeTracker* Dce2Smb2SessionTracker::find_tree_for_message( + uint64_t message_id) +{ + for (auto it_tree : connected_trees) + { + Dce2Smb2RequestTracker* request = it_tree.second->find_request(message_id); + if (request) + return it_tree.second; + } + return nullptr; +} + +void Dce2Smb2SessionTracker::process(uint16_t command, uint8_t command_type, + const Smb2Hdr* smb_header, const uint8_t* end) +{ + Dce2Smb2TreeTracker* tree = nullptr; + uint32_t tree_id = Smb2Tid(smb_header); + + if (tree_id) + { + auto it_tree = connected_trees.find(tree_id); + if (it_tree != connected_trees.end()) + tree = it_tree->second; + } + else + { + //async response case + tree = find_tree_for_message(Smb2Mid(smb_header)); + } + + switch (command) + { + case SMB2_COM_TREE_CONNECT: + { + uint8_t share_type = ((const Smb2TreeConnectResponseHdr*) + ((const uint8_t*)smb_header + SMB2_HEADER_LENGTH))->share_type; + connect_tree(tree_id, share_type); + } + break; + + case SMB2_COM_TREE_DISCONNECT: + if (tree) + { + delete tree; + connected_trees.erase(tree_id); + } + else + dce2_smb_stats.v2_tree_discn_ignored++; + break; + + //for all other cases, tree tracker should handle the command + case SMB2_COM_CREATE: + if (!tree and SMB2_CMD_TYPE_REQUEST == command_type) + { + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, + "%s_REQ: mid-stream session detected\n", + smb2_command_string[command]); + tree = connect_tree(tree_id); + if (!tree) + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, + "%s_REQ: insert tree tracker failed\n", + smb2_command_string[command]); + } + // fallthrough + default: + if (tree) + tree->process(command, command_type, smb_header, end); + else + { + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, + "%s: tree tracker missing\n", smb2_command_string[command]); + dce2_smb_stats.v2_tree_ignored++; + } + break; + } +} + +Dce2Smb2TreeTracker* Dce2Smb2SessionTracker::connect_tree(uint32_t tree_id, + uint8_t share_type) +{ + Dce2Smb2SessionData* current_flow = get_current_flow(); + if ((SMB2_SHARE_TYPE_DISK == share_type) and (-1 == current_flow->get_max_file_depth()) and + (-1 == current_flow->get_smb_file_depth())) + { + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, "Not inserting TID (%u) " + "because it's not IPC and not inspecting normal file data.\n", tree_id); + dce2_smb_stats.v2_tree_cnct_ignored++; + return nullptr; + } + Dce2Smb2TreeTracker* tree = nullptr; + auto it_tree = connected_trees.find(tree_id); + if (it_tree != connected_trees.end()) + tree = it_tree->second; + if (!tree) + { + tree = new Dce2Smb2TreeTracker(tree_id, this, share_type); + connected_trees.insert(std::make_pair(tree_id, tree)); + } + return tree; +} + +void Dce2Smb2SessionTracker::attach_flow(Smb2FlowKey flow_key, + Dce2Smb2SessionData* ssd) +{ + attached_flows.insert(std::make_pair(flow_key,ssd)); +} + +bool Dce2Smb2SessionTracker::detach_flow(Smb2FlowKey& flow_key) +{ + attached_flows.erase(flow_key); + return (0 == attached_flows.size()); +} + +// Session Tracker is created and destroyed only from session cache +Dce2Smb2SessionTracker::~Dce2Smb2SessionTracker(void) +{ + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, "session tracker %" PRIu64 + " terminating\n", session_id); + auto it_tree = connected_trees.begin(); + while (it_tree != connected_trees.end()) + { + Dce2Smb2TreeTracker* tree = it_tree->second; + it_tree = connected_trees.erase(it_tree); + delete tree; + } + + for (auto it_flow : attached_flows) + it_flow.second->remove_session(session_id); + + memory::MemoryCap::update_deallocations(sizeof(*this)); +} + diff --git a/src/service_inspectors/dce_rpc/dce_smb2_session.h b/src/service_inspectors/dce_rpc/dce_smb2_session.h new file mode 100644 index 000000000..e74720619 --- /dev/null +++ b/src/service_inspectors/dce_rpc/dce_smb2_session.h @@ -0,0 +1,61 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2021 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_session.h author Dipta Pandit + +#ifndef DCE_SMB2_SESSION_H +#define DCE_SMB2_SESSION_H + +// This provides session tracker for SMBv2 + +#include "dce_smb2.h" +#include "dce_smb2_tree.h" + +uint32_t Smb2Tid(const Smb2Hdr* hdr); + +class Dce2Smb2SessionTracker +{ +public: + Dce2Smb2SessionTracker() + { + session_id = 0; + session_key = { }; + memory::MemoryCap::update_allocations(sizeof(*this)); + } + + ~Dce2Smb2SessionTracker(); + void init(uint64_t, const Smb2SessionKey&); + void attach_flow(Smb2FlowKey, Dce2Smb2SessionData*); + bool detach_flow(Smb2FlowKey&); + void process(uint16_t, uint8_t, const Smb2Hdr*, const uint8_t*); + void disconnect_tree(uint32_t tree_id) { connected_trees.erase(tree_id); } + Dce2Smb2SessionData* get_current_flow(); + Smb2SessionKey get_key() { return session_key; } + Dce2Smb2SessionDataMap get_attached_flows() { return attached_flows; } + Dce2Smb2TreeTracker* connect_tree(uint32_t, uint8_t=SMB2_SHARE_TYPE_DISK); + +private: + Dce2Smb2TreeTracker* find_tree_for_message(uint64_t); + uint64_t session_id; + Smb2SessionKey session_key; + Dce2Smb2SessionDataMap attached_flows; + Dce2Smb2TreeTrackerMap connected_trees; +}; + +#endif + diff --git a/src/service_inspectors/dce_rpc/dce_smb2_session_cache.h b/src/service_inspectors/dce_rpc/dce_smb2_session_cache.h new file mode 100644 index 000000000..f3839771c --- /dev/null +++ b/src/service_inspectors/dce_rpc/dce_smb2_session_cache.h @@ -0,0 +1,60 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2021 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_session_cache.h author Bhargava Jandhyala + +#ifndef DCE_SMB2_SESSION_CACHE_H +#define DCE_SMB2_SESSION_CACHE_H + +// This provides a wrapper over LRU cache shared for SMBv2 Session trackers + +#include "hash/lru_cache_shared.h" + +#include "dce_smb2_session.h" + +#define SMB_AVG_FILES_PER_SESSION 5 + +template +class Dce2Smb2SharedCache : public LruCacheShared +{ +public: + Dce2Smb2SharedCache() = delete; + Dce2Smb2SharedCache(const Dce2Smb2SharedCache& arg) = delete; + Dce2Smb2SharedCache& operator=(const Dce2Smb2SharedCache& arg) = delete; + Dce2Smb2SharedCache(const size_t initial_size) : + LruCacheShared(initial_size) { } + virtual ~Dce2Smb2SharedCache() { } + + Value* find_session(Key key) + { return this->find(key).get(); } + Value* find_else_create_session(Key key) + { return this->find_else_create(key, nullptr).get(); } +}; + +using Dce2Smb2SessionCache = + Dce2Smb2SharedCache; + +extern THREAD_LOCAL Dce2Smb2SessionCache* smb2_session_cache; + +inline void DCE2_SmbSessionCacheInit(const size_t cache_size) +{ + smb2_session_cache = new Dce2Smb2SessionCache(cache_size); +} + +#endif + diff --git a/src/service_inspectors/dce_rpc/dce_smb2_tree.cc b/src/service_inspectors/dce_rpc/dce_smb2_tree.cc new file mode 100644 index 000000000..76b3c9539 --- /dev/null +++ b/src/service_inspectors/dce_rpc/dce_smb2_tree.cc @@ -0,0 +1,452 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2021 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_tree.cc author Dipta Pandit + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "dce_smb2_tree.h" + +#include "dce_smb2_session.h" + +using namespace snort; + +#define SMB2_CREATE_DURABLE_RECONNECT "DHnC" +#define SMB2_CREATE_DURABLE_RECONNECT_V2 "DH2C" + +uint64_t Smb2Mid(const Smb2Hdr* hdr) +{ + return alignedNtohq(&(hdr->message_id)); +} + +Dce2Smb2FileTracker* Dce2Smb2TreeTracker::find_file(uint64_t file_id) +{ + auto it_file = opened_files.find(file_id); + if (it_file != opened_files.end()) + return it_file->second; + return nullptr; +} + +void Dce2Smb2TreeTracker::close_file(uint64_t file_id, bool destroy) +{ + auto it_file = opened_files.find(file_id); + if (it_file != opened_files.end()) + { + Dce2Smb2FileTracker* file = it_file->second; + if (opened_files.erase(file_id) and destroy) + delete file; + } +} + +Dce2Smb2RequestTracker* Dce2Smb2TreeTracker::find_request(uint64_t message_id) +{ + auto request_it = active_requests.find(message_id); + return (request_it == active_requests.end()) ? + nullptr : request_it->second; +} + +bool Dce2Smb2TreeTracker::remove_request(uint64_t message_id) +{ + auto request_it = active_requests.find(message_id); + if (request_it != active_requests.end()) + { + delete request_it->second; + return active_requests.erase(message_id); + } + return false; +} + +void Dce2Smb2TreeTracker::process_set_info_request(const Smb2Hdr* smb_header) +{ + const uint8_t* smb_data = (const uint8_t*)smb_header + SMB2_HEADER_LENGTH; + const Smb2SetInfoRequestHdr* set_info_hdr = (const Smb2SetInfoRequestHdr*)smb_data; + + if (set_info_hdr->file_info_class == SMB2_FILE_ENDOFFILE_INFO) + { + uint64_t file_size = alignedNtohq((const uint64_t*)((const uint8_t*) + set_info_hdr + SMB2_SET_INFO_REQUEST_STRUC_SIZE - 1)); + uint64_t file_id = alignedNtohq(&(set_info_hdr->fileId_persistent)); + Dce2Smb2FileTracker* file_tracker = find_file(file_id); + if (file_tracker) + file_tracker->set_info(nullptr, 0, file_size); + else + { + dce2_smb_stats.v2_stinf_req_ftrkr_misng++; + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, "%s_REQ: ftracker missing\n", + smb2_command_string[SMB2_COM_SET_INFO]); + } + } + else + { + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, "%s_REQ: header error\n", + smb2_command_string[SMB2_COM_SET_INFO]); + dce2_smb_stats.v2_stinf_req_hdr_err++; + } +} + +void Dce2Smb2TreeTracker::process_close_request(const Smb2Hdr* smb_header) +{ + const uint8_t* smb_data = (const uint8_t*)smb_header + SMB2_HEADER_LENGTH; + uint64_t file_id = alignedNtohq(&(((const Smb2CloseRequestHdr*) + smb_data)->fileId_persistent)); + Dce2Smb2FileTracker* file_tracker = find_file(file_id); + if (!file_tracker) + { + dce2_smb_stats.v2_cls_req_ftrkr_misng++; + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, "%s_REQ: ftracker missing %" PRIu64 "\n", + smb2_command_string[SMB2_COM_CLOSE], file_id); + return; + } + if (file_tracker->close()) + close_file(file_id); +} + +uint64_t Dce2Smb2TreeTracker::get_durable_file_id( + const Smb2CreateRequestHdr* smb_create_hdr, const uint8_t* end) +{ + const uint8_t* data = (const uint8_t*)smb_create_hdr + + alignedNtohl(&smb_create_hdr->create_contexts_offset) - SMB2_HEADER_LENGTH; + uint32_t remaining = alignedNtohl(&smb_create_hdr->create_contexts_length); + + while (remaining > sizeof(Smb2CreateContextHdr) and data < end) + { + const Smb2CreateContextHdr* context = (const Smb2CreateContextHdr*)data; + uint32_t next = alignedNtohl(&context->next); + uint16_t name_offset = alignedNtohs(&context->name_offset); + uint16_t name_length = alignedNtohs(&context->name_length); + uint16_t data_offset = alignedNtohs(&context->data_offset); + uint32_t data_length = alignedNtohl(&context->data_length); + + /* Check for general error condition */ + if (((next & 0x7) != 0) or (next > remaining) or (name_offset != 16) or + (name_length != 4) or (name_offset + name_length > remaining) or + ((data_offset & 0x7) != 0) or + (data_offset and (data_offset < name_offset + name_length)) or + (data_offset > remaining) or (data_offset + data_length > remaining)) + { + return 0; + } + + if ((strncmp((const char*)context+name_offset, + SMB2_CREATE_DURABLE_RECONNECT_V2, name_length) == 0) or + (strncmp((const char*)context+name_offset, + SMB2_CREATE_DURABLE_RECONNECT, name_length) == 0)) + { + return alignedNtohq((const uint64_t*)(((const uint8_t*)context) + + data_offset)); + } + + if (!next) + break; + + data += next; + remaining -= next; + } + return 0; +} + +void Dce2Smb2TreeTracker::process_create_response(uint64_t message_id, + const Smb2Hdr* smb_header) +{ + const uint8_t* smb_data = (const uint8_t*)smb_header + SMB2_HEADER_LENGTH; + const Smb2CreateResponseHdr* create_res_hdr = (const Smb2CreateResponseHdr*)smb_data; + uint64_t file_size = 0; + uint64_t file_id = alignedNtohq((const uint64_t*)(&(create_res_hdr->fileId_persistent))); + if (create_res_hdr->end_of_file) + file_size = alignedNtohq((const uint64_t*)(&(create_res_hdr->end_of_file))); + if (create_res_hdr->file_attributes & SMB2_CREATE_RESPONSE_DIRECTORY) + { + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, "%s_RESP: not processing for directory\n", + smb2_command_string[SMB2_COM_CREATE]); + close_file(file_id); + } + else + { + Dce2Smb2RequestTracker* create_request = find_request(message_id); + if (create_request) + { + Dce2Smb2FileTracker* file_tracker = find_file(file_id); + if (!file_tracker) + { + file_tracker = new Dce2Smb2FileTracker(file_id, this); + opened_files.insert(std::make_pair(file_id, file_tracker)); + } + file_tracker->set_info(create_request->get_file_name(), + create_request->get_file_name_size(), file_size, true); + create_request->reset_file_name(); + } + else + { + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, "%s_RESP: req tracker missing\n", + smb2_command_string[SMB2_COM_CREATE]); + dce2_smb_stats.v2_crt_rtrkr_misng++; + } + } +} + +void Dce2Smb2TreeTracker::process_create_request(uint64_t message_id, + const Smb2Hdr* smb_header, const uint8_t* end) +{ + const uint8_t* smb_data = (const uint8_t*)smb_header + SMB2_HEADER_LENGTH; + const Smb2CreateRequestHdr* create_req_hdr = (const Smb2CreateRequestHdr*)smb_data; + if (alignedNtohs(&(create_req_hdr->name_offset)) <= SMB2_HEADER_LENGTH) + { + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, "%s_REQ: name_offset %" PRIu16 "\n", + smb2_command_string[SMB2_COM_CREATE], create_req_hdr->name_offset); + dce2_smb_stats.v2_crt_req_hdr_err++; + return; + } + const uint8_t* file_name_offset = (const uint8_t*)smb_header + + create_req_hdr->name_offset; + uint16_t file_name_size = alignedNtohs(&(create_req_hdr->name_length)); + if (!file_name_size or (file_name_offset >= end) or + (file_name_offset + file_name_size > end)) + { + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, + "%s_REQ: invalid file name data seen with size %" PRIu16 "\n", + smb2_command_string[SMB2_COM_CREATE], file_name_size); + dce2_smb_stats.v2_crt_inv_file_data++; + return; + } + + uint16_t name_len = 0; + char* file_name = get_smb_file_name(file_name_offset, file_name_size, true, &name_len); + //keep a request tracker with the available info + Dce2Smb2RequestTracker* create_request = new Dce2Smb2RequestTracker(file_name, name_len); + store_request(message_id, create_request); + //check if file_id is available form a durable reconnect request. + //if present we can create a file tracker right now. + //mostly this is the case for compound request. + uint64_t file_id = get_durable_file_id(create_req_hdr, end); + if (file_id) + { + Dce2Smb2FileTracker* file_tracker = find_file(file_id); + if (!file_tracker) + { + file_tracker = new Dce2Smb2FileTracker(file_id, this); + file_tracker->set_info(file_name, name_len, 0, true); + create_request->reset_file_name(); + opened_files.insert(std::make_pair(file_id, file_tracker)); + } + } +} + +void Dce2Smb2TreeTracker::process_read_response(uint64_t message_id, + const Smb2Hdr* smb_header, const uint8_t* end) +{ + const uint8_t* smb_data = (const uint8_t*)smb_header + SMB2_HEADER_LENGTH; + const Smb2ReadResponseHdr* read_resp_hdr = (const Smb2ReadResponseHdr*)smb_data; + + Dce2Smb2RequestTracker* read_request = find_request(message_id); + if (!read_request) + { + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, + "SMB2_COM_READ_RESP: request tracker missing\n"); + dce2_smb_stats.v2_read_rtrkr_misng++; + return; + } + uint16_t data_offset = alignedNtohs((const uint16_t*)(&(read_resp_hdr->data_offset))); + Dce2Smb2SessionData* current_flow = parent_session->get_current_flow(); + if (data_offset + (const uint8_t*)smb_header > end) + { + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, "SMB2_COM_READ_RESP: bad offset\n"); + if (current_flow) + dce_alert(GID_DCE2, DCE2_SMB_BAD_OFF, (dce2CommonStats*)&dce2_smb_stats, + *current_flow->get_dce2_session_data()); + } + Dce2Smb2FileTracker* file_tracker = find_file(read_request->get_file_id()); + if (file_tracker) + { + const uint8_t* file_data = (const uint8_t*)read_resp_hdr + + SMB2_READ_RESPONSE_STRUC_SIZE - 1; + int data_size = end - file_data; + if (file_tracker->process_data(file_data, data_size, read_request->get_offset())) + { + if ((uint32_t)data_size < alignedNtohl((const uint32_t*)&(read_resp_hdr->length))) + { + file_tracker->accept_raw_data_from(current_flow); + } + } + else + close_file(file_tracker->get_file_id()); + } + else + { + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, + "SMB2_COM_READ_RESP: file tracker missing\n"); + } +} + +void Dce2Smb2TreeTracker::process_read_request(uint64_t message_id, + const Smb2Hdr* smb_header) +{ + const uint8_t* smb_data = (const uint8_t*)smb_header + SMB2_HEADER_LENGTH; + const Smb2ReadRequestHdr* read_req_hdr = (const Smb2ReadRequestHdr*)smb_data; + uint64_t file_id = alignedNtohq((const uint64_t*)(&(read_req_hdr->fileId_persistent))); + uint64_t offset = alignedNtohq((const uint64_t*)(&(read_req_hdr->offset))); + Dce2Smb2RequestTracker* read_request = new Dce2Smb2RequestTracker(file_id, offset); + store_request(message_id, read_request); +} + +void Dce2Smb2TreeTracker::process_write_request(uint64_t message_id, + const Smb2Hdr* smb_header, const uint8_t* end) +{ + const uint8_t* smb_data = (const uint8_t*)smb_header + SMB2_HEADER_LENGTH; + const Smb2WriteRequestHdr* write_req_hdr = (const Smb2WriteRequestHdr*)smb_data; + uint64_t file_id = alignedNtohq((const uint64_t*)(&(write_req_hdr->fileId_persistent))); + Dce2Smb2SessionData* current_flow = parent_session->get_current_flow(); + if ((alignedNtohs((const uint16_t*)(&(write_req_hdr->data_offset))) + + (const uint8_t*)smb_header > end) and current_flow) + { + dce_alert(GID_DCE2, DCE2_SMB_BAD_OFF, (dce2CommonStats*)&dce2_smb_stats, + *current_flow->get_dce2_session_data()); + } + //track this request to clean up opened file in case of error response + Dce2Smb2RequestTracker* write_request = new Dce2Smb2RequestTracker(file_id); + store_request(message_id, write_request); + const uint8_t* file_data = (const uint8_t*)write_req_hdr + SMB2_WRITE_REQUEST_STRUC_SIZE - 1; + Dce2Smb2FileTracker* file_tracker = find_file(file_id); + if (file_tracker) + { + file_tracker->set_direction(FILE_UPLOAD); + int data_size = end - file_data; + uint64_t offset = alignedNtohq((const uint64_t*)(&(write_req_hdr->offset))); + if (file_tracker->process_data(file_data, data_size, offset)) + { + if ((uint32_t)data_size < alignedNtohl((const uint32_t*)&(write_req_hdr->length))) + { + file_tracker->accept_raw_data_from(current_flow); + } + } + else + close_file(file_tracker->get_file_id()); + } + else + { + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, + "SMB2_COM_WRITE_REQ: file tracker missing\n"); + } +} + +void Dce2Smb2TreeTracker::process(uint16_t command, uint8_t command_type, + const Smb2Hdr* smb_header, const uint8_t* end) +{ + Dce2Smb2SessionData* current_flow = parent_session->get_current_flow(); + if (SMB2_CMD_TYPE_REQUEST == command_type and current_flow and + active_requests.size() >= current_flow->get_max_outstanding_requests()) + { + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, + "%s_REQ: max req exceeded\n", smb2_command_string[command]); + dce_alert(GID_DCE2, DCE2_SMB_MAX_REQS_EXCEEDED, (dce2CommonStats*)&dce2_smb_stats, + *current_flow->get_dce2_session_data()); + + return; + } + uint64_t message_id = Smb2Mid(smb_header); + + switch (command) + { + case SMB2_COM_CREATE: + if (SMB2_CMD_TYPE_ERROR_RESPONSE == command_type) + { + dce2_smb_stats.v2_crt_err_resp++; + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, + "%s_RESP: error\n", smb2_command_string[command]); + } + else if (SMB2_CMD_TYPE_REQUEST == command_type) + { + if (SMB2_SHARE_TYPE_DISK != share_type) + { + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, "%s_REQ:" + " ignored for ipc share\n", smb2_command_string[command]); + dce2_smb_stats.v2_crt_req_ipc++; + return; + } + process_create_request(message_id, smb_header, end); + } + else if (SMB2_CMD_TYPE_RESPONSE == command_type) + process_create_response(message_id, smb_header); + break; + case SMB2_COM_CLOSE: + process_close_request(smb_header); + break; + case SMB2_COM_SET_INFO: + process_set_info_request(smb_header); + break; + case SMB2_COM_READ: + if (SMB2_CMD_TYPE_ERROR_RESPONSE == command_type) + { + dce2_smb_stats.v2_read_err_resp++; + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, + "%s_RESP: error\n", smb2_command_string[command]); + Dce2Smb2RequestTracker* request = find_request(message_id); + if (request) + close_file(request->get_file_id()); + } + else if (SMB2_CMD_TYPE_REQUEST == command_type) + process_read_request(message_id, smb_header); + else if (SMB2_CMD_TYPE_RESPONSE == command_type) + process_read_response(message_id, smb_header, end); + break; + case SMB2_COM_WRITE: + if (SMB2_CMD_TYPE_ERROR_RESPONSE == command_type) + { + dce2_smb_stats.v2_wrt_err_resp++; + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, + "%s_RESP: error\n", smb2_command_string[command]); + Dce2Smb2RequestTracker* request = find_request(message_id); + if (request) + close_file(request->get_file_id()); + } + else if (SMB2_CMD_TYPE_REQUEST == command_type) + process_write_request(message_id, smb_header, end); + break; + } + if (SMB2_CMD_TYPE_RESPONSE == command_type or SMB2_CMD_TYPE_ERROR_RESPONSE == command_type) + remove_request(message_id); +} + +Dce2Smb2TreeTracker::~Dce2Smb2TreeTracker(void) +{ + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, + "tree tracker %" PRIu32 " terminating\n", tree_id); + if (active_requests.size()) + { + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, + "cleanup pending requests for below MIDs:\n"); + for (auto it_request : active_requests) + { + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, + "mid %" PRIu64 "\n", it_request.first); + delete it_request.second; + } + } + + auto it_file = opened_files.begin(); + while (it_file != opened_files.end()) + { + Dce2Smb2FileTracker* file = it_file->second; + it_file = opened_files.erase(it_file); + delete file; + } + parent_session->disconnect_tree(tree_id); + memory::MemoryCap::update_deallocations(sizeof(*this)); +} + diff --git a/src/service_inspectors/dce_rpc/dce_smb2_tree.h b/src/service_inspectors/dce_rpc/dce_smb2_tree.h new file mode 100644 index 000000000..84dfba4ee --- /dev/null +++ b/src/service_inspectors/dce_rpc/dce_smb2_tree.h @@ -0,0 +1,83 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2021 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_tree.h author Dipta Pandit + +#ifndef DCE_SMB2_TREE_H +#define DCE_SMB2_TREE_H + +// This provides tree trackers for SMBv2. +// Tree trackers are used to identify and track an opened share + +#include "dce_smb2.h" +#include "dce_smb2_file.h" +#include "dce_smb2_request.h" + +uint64_t Smb2Mid(const Smb2Hdr* hdr); + +class Dce2Smb2SessionTracker; + +class Dce2Smb2TreeTracker +{ +public: + Dce2Smb2TreeTracker() = delete; + Dce2Smb2TreeTracker(const Dce2Smb2TreeTracker&) = delete; + Dce2Smb2TreeTracker& operator=(const Dce2Smb2TreeTracker&) = delete; + + Dce2Smb2TreeTracker(uint32_t tree_id_v, Dce2Smb2SessionTracker* p_session, uint8_t sharetype) + : tree_id(tree_id_v), share_type(sharetype), parent_session(p_session) + { + debug_logf(dce_smb_trace, GET_CURRENT_PACKET, + "tree tracker %" PRIu32 " created\n", tree_id); + memory::MemoryCap::update_allocations(sizeof(*this)); + } + + ~Dce2Smb2TreeTracker(); + + void open_file(uint64_t); + void close_file(uint64_t, bool=true); + Dce2Smb2FileTracker* find_file(uint64_t); + Dce2Smb2RequestTracker* find_request(uint64_t); + void process(uint16_t, uint8_t, const Smb2Hdr*, const uint8_t*); + Dce2Smb2SessionTracker* get_parent() { return parent_session; } + +private: + void process_set_info_request(const Smb2Hdr*); + void process_close_request(const Smb2Hdr*); + void process_create_response(uint64_t, const Smb2Hdr*); + void process_create_request(uint64_t, const Smb2Hdr*, const uint8_t*); + void process_read_response(uint64_t, const Smb2Hdr*, const uint8_t*); + void process_read_request(uint64_t, const Smb2Hdr*); + void process_write_request(uint64_t, const Smb2Hdr*, const uint8_t*); + uint64_t get_durable_file_id(const Smb2CreateRequestHdr*, const uint8_t*); + bool remove_request(uint64_t); + void store_request(uint64_t message_id, Dce2Smb2RequestTracker* request) + { active_requests.insert(std::make_pair(message_id, request)); } + + uint32_t tree_id; + uint8_t share_type; + Dce2Smb2FileTrackerMap opened_files; + Dce2Smb2RequestTrackerMap active_requests; + Dce2Smb2SessionTracker* parent_session; +}; + +using Dce2Smb2TreeTrackerMap = + std::unordered_map >; + +#endif + diff --git a/src/service_inspectors/dce_rpc/dce_smb2_utils.cc b/src/service_inspectors/dce_rpc/dce_smb2_utils.cc deleted file mode 100644 index 6f35b45ab..000000000 --- a/src/service_inspectors/dce_rpc/dce_smb2_utils.cc +++ /dev/null @@ -1,116 +0,0 @@ -//-------------------------------------------------------------------------- -// 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 "flow/flow_key.h" -#include "main/snort_debug.h" - -using namespace snort; - -size_t session_cache_size; -THREAD_LOCAL SmbSessionCache* smb2_session_cache; - -Smb2SidHashKey get_key(uint64_t sid) -{ - Smb2SidHashKey key; - Flow* flow = DetectionEngine::get_current_packet()->flow; - memcpy(key.cip, flow->client_ip.get_ip6_ptr(), 4*sizeof(uint32_t)); - memcpy(key.sip, flow->server_ip.get_ip6_ptr(), 4*sizeof(uint32_t)); - key.sid = sid; - key.cgroup = flow->client_group; - key.sgroup = flow->server_group; - key.asid = flow->key->addressSpaceId; - key.padding = 0; - return key; -} - -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); - stracker->session_key = get_key(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, DetectionEngine::get_current_packet(), - "Not inserting TID (%u) because it's " - "not IPC and not inspecting normal file data.\n", tid); - return nullptr; - } - - DCE2_Smb2TreeTracker* ttracker = str->findTtracker(tid); - if (!ttracker) - { - ttracker = new DCE2_Smb2TreeTracker(tid, share_type); - str->insertTtracker(tid, ttracker); - } - - return ttracker; -} - -void DCE2_Smb2RemoveAllSession(DCE2_Smb2SsnData* ssd) -{ - ssd->ftracker_tcp = nullptr; - - // iterate over smb sessions for this tcp connection and cleanup its instance from them - auto all_session_trackers = ssd->session_trackers.get_all_entry(); - for ( auto& h : all_session_trackers ) - { - ssd->session_trackers.Remove(h.second->session_id); // remove session tracker from this - // tcp conn - h.second->removeConnTracker(ssd->flow_key); // remove tcp connection from session tracker - if (!h.second->getConnTrackerSize()) // if no tcp connection present in session tracker, - // delete session tracker - { - DCE2_SmbSessionCacheRemove(h.second->session_key); - } - } -} diff --git a/src/service_inspectors/dce_rpc/dce_smb2_utils.h b/src/service_inspectors/dce_rpc/dce_smb2_utils.h deleted file mode 100644 index ec83b1ed1..000000000 --- a/src/service_inspectors/dce_rpc/dce_smb2_utils.h +++ /dev/null @@ -1,122 +0,0 @@ -//-------------------------------------------------------------------------- -// 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" - -Smb2SidHashKey get_key(uint64_t 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) - { - } - ~SmbSessionCache_map() override = default; -}; - -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(get_key(sid))).get(); -} - -inline DCE2_Smb2SessionTracker* DCE2_SmbSessionCacheFindElseCreate(uint64_t sid, - bool* entry_created) -{ - return (smb2_session_cache->find_else_create(get_key(sid), entry_created)).get(); -} - -inline bool DCE2_SmbSessionCacheRemove(Smb2SidHashKey key) -{ - return smb2_session_cache->remove(key); -} - -// 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) -{ - // add ssd in session tracker's tcp trackers database - stracker->insertConnTracker(ssd->flow_key, ssd); - - 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_Smb2SessionTracker* DCE2_Smb2FindElseCreateSid(DCE2_Smb2SsnData*, const uint64_t sid); - -DCE2_Ret DCE2_Smb2InitData(DCE2_Smb2SsnData*); - -void DCE2_Smb2RemoveAllSession(DCE2_Smb2SsnData* ssd); - -#endif - diff --git a/src/service_inspectors/dce_rpc/dce_smb_commands.cc b/src/service_inspectors/dce_rpc/dce_smb_commands.cc index bfd6a75fa..94537a48f 100644 --- a/src/service_inspectors/dce_rpc/dce_smb_commands.cc +++ b/src/service_inspectors/dce_rpc/dce_smb_commands.cc @@ -567,7 +567,7 @@ DCE2_Ret DCE2_SmbOpen(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr, dce2_move(nb_ptr, nb_len, 1); ssd->cur_rtracker->file_name = - DCE2_SmbGetFileName(nb_ptr, nb_len, SmbUnicode(smb_hdr), + get_smb_file_name(nb_ptr, nb_len, SmbUnicode(smb_hdr), &ssd->cur_rtracker->file_name_size); } @@ -625,7 +625,7 @@ DCE2_Ret DCE2_SmbCreate(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr, dce2_move(nb_ptr, nb_len, 1); ssd->cur_rtracker->file_name = - DCE2_SmbGetFileName(nb_ptr, nb_len, SmbUnicode(smb_hdr), + get_smb_file_name(nb_ptr, nb_len, SmbUnicode(smb_hdr), &ssd->cur_rtracker->file_name_size); } @@ -856,7 +856,7 @@ DCE2_Ret DCE2_SmbCreateNew(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr, dce2_move(nb_ptr, nb_len, 1); ssd->cur_rtracker->file_name = - DCE2_SmbGetFileName(nb_ptr, nb_len, SmbUnicode(smb_hdr), + get_smb_file_name(nb_ptr, nb_len, SmbUnicode(smb_hdr), &ssd->cur_rtracker->file_name_size); } @@ -1069,7 +1069,7 @@ DCE2_Ret DCE2_SmbOpenAndX(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr, if (ssd->cur_rtracker->file_name == nullptr) { ssd->cur_rtracker->file_name = - DCE2_SmbGetFileName(nb_ptr, nb_len, unicode, &ssd->cur_rtracker->file_name_size); + get_smb_file_name(nb_ptr, nb_len, unicode, &ssd->cur_rtracker->file_name_size); } } @@ -1930,7 +1930,7 @@ DCE2_Ret DCE2_SmbNtCreateAndX(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr, if (ssd->cur_rtracker->file_name == nullptr) { ssd->cur_rtracker->file_name = - DCE2_SmbGetFileName(nb_ptr, file_name_length, unicode, + get_smb_file_name(nb_ptr, file_name_length, unicode, &ssd->cur_rtracker->file_name_size); } diff --git a/src/service_inspectors/dce_rpc/dce_smb_common.cc b/src/service_inspectors/dce_rpc/dce_smb_common.cc new file mode 100644 index 000000000..034fbdb5d --- /dev/null +++ b/src/service_inspectors/dce_rpc/dce_smb_common.cc @@ -0,0 +1,198 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2021 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_smb_common.cc author Dipta Pandit + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "dce_smb_common.h" + +#include "file_api/file_flows.h" +#include "file_api/file_service.h" +#include "memory/memory_cap.h" + +#include "dce_smb1.h" +#include "dce_smb2.h" + +using namespace snort; + +THREAD_LOCAL dce2SmbStats dce2_smb_stats; +THREAD_LOCAL ProfileStats dce2_smb_pstat_main; + +//Dce2SmbFlowData members + +unsigned Dce2SmbFlowData::inspector_id = 0; + +Dce2SmbFlowData::Dce2SmbFlowData(Dce2SmbSessionData* ssd_v) : 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; + ssd = ssd_v; + memory::MemoryCap::update_allocations(sizeof(*this)); +} + +Dce2SmbSessionData* Dce2SmbFlowData::upgrade(const Packet* p) +{ + dce2SmbProtoConf* config = + (dce2SmbProtoConf*)ssd->get_dce2_session_data()->config; + delete ssd; + ssd = new Dce2Smb2SessionData(p, config); + return ssd; +} + +void Dce2SmbFlowData::handle_retransmit(Packet* p) +{ + FilePosition position = get_file_position(p); + if (!(SNORT_FILE_FULL == position or SNORT_FILE_END == position)) + return; + FileContext* context = get_smb_file_context(p); + FileVerdict verdict = context ? context->verdict : FILE_VERDICT_UNKNOWN; + ssd->handle_retransmit(position, verdict); +} + +Dce2SmbFlowData::~Dce2SmbFlowData() +{ + delete ssd; + assert(dce2_smb_stats.concurrent_sessions > 0); + dce2_smb_stats.concurrent_sessions--; + memory::MemoryCap::update_deallocations(sizeof(*this)); +} + +//Dce2SmbSessionData members + +Dce2SmbSessionData::Dce2SmbSessionData(const Packet* p, + const dce2SmbProtoConf* config) +{ + sd = { }; + policy = { }; + tcp_flow = p->flow; + DCE2_ResetRopts(&sd, p); + sd.trans = DCE2_TRANS_TYPE__SMB; + sd.server_policy = config->common.policy; + sd.client_policy = DCE2_POLICY__WINXP; + sd.config = (void*)config; + dialect_index = DCE2_SENTINEL; + max_file_depth = FileService::get_max_file_depth(); + dce2_smb_stats.smb_sessions++; +} + +//Helper functions + +static inline DCE2_SmbVersion get_smb_version(const Packet* p) +{ + // Only check reassembled SMB2 packet + if ( p->has_paf_payload() and + (p->dsize > sizeof(NbssHdr) + DCE2_SMB_ID_SIZE)) + { + const SmbNtHdr* smb_hdr = (const SmbNtHdr*)(p->data + sizeof(NbssHdr)); + uint32_t smb_version_id = SmbId(smb_hdr); + + if (smb_version_id == DCE2_SMB_ID) + return DCE2_SMB_VERSION_1; + else if (smb_version_id == DCE2_SMB2_ID) + return DCE2_SMB_VERSION_2; + } + + return DCE2_SMB_VERSION_NULL; +} + +Dce2SmbSessionData* create_new_smb_session(const Packet* p, + dce2SmbProtoConf* config) +{ + DCE2_SmbVersion smb_version = get_smb_version(p); + + if (DCE2_SMB_VERSION_NULL == smb_version) + return nullptr; + + Dce2SmbSessionData* ssd = (DCE2_SMB_VERSION_1 == smb_version) ? + (Dce2SmbSessionData*)new Dce2Smb1SessionData(p, config) : + (Dce2SmbSessionData*)new Dce2Smb2SessionData(p, config); + + Dce2SmbFlowData* flow_data = new Dce2SmbFlowData(ssd); + p->flow->set_flow_data(flow_data); + + return ssd; +} + +DCE2_SsnData* get_dce2_session_data(snort::Flow* flow) +{ + Dce2SmbFlowData* fd = (Dce2SmbFlowData*)flow->get_flow_data(Dce2SmbFlowData::inspector_id); + return fd ? fd->get_smb_session_data()->get_dce2_session_data() : nullptr; +} + +inline FileContext* get_smb_file_context(const Packet* p) +{ + FileFlows* file_flows = FileFlows::get_file_flows(p->flow); + return file_flows ? file_flows->get_current_file_context() : nullptr; +} + +FileContext* get_smb_file_context(uint64_t file_id, + uint64_t multi_file_processing_id, bool to_create) +{ + 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, to_create, multi_file_processing_id); +} + +char* get_smb_file_name(const uint8_t* data, uint32_t data_len, bool unicode, + uint16_t* file_name_len) +{ + const uint8_t inc = unicode ? 2 : 1; + if (data_len < inc) + return nullptr; + + const uint32_t max_len = unicode ? data_len - 1 : data_len; + // Move forward. Don't know if the end of data is actually + // the end of the string. + uint32_t i; + for (i = 0; i < max_len; i += inc) + { + uint16_t uchar = unicode ? extract_16bits(data + i) : data[i]; + if (uchar == 0) + break; + } + + char* fname = nullptr; + const uint32_t real_len = i; + + if (unicode) + { + fname = (char*)snort_calloc(real_len + UTF_16_LE_BOM_LEN + 2); + memcpy(fname, UTF_16_LE_BOM, UTF_16_LE_BOM_LEN);//Prepend with BOM + memcpy(fname + UTF_16_LE_BOM_LEN, data, real_len); + *file_name_len = real_len + UTF_16_LE_BOM_LEN; + } + else + { + fname = (char*)snort_alloc(real_len + 1); + memcpy(fname, data, real_len); + fname[real_len] = 0; + *file_name_len = real_len; + } + return fname; +} + diff --git a/src/service_inspectors/dce_rpc/dce_smb_common.h b/src/service_inspectors/dce_rpc/dce_smb_common.h new file mode 100644 index 000000000..19c33384c --- /dev/null +++ b/src/service_inspectors/dce_rpc/dce_smb_common.h @@ -0,0 +1,290 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2021 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_smb_common.h author Dipta Pandit + +#ifndef DCE_SMB_COMMON_H +#define DCE_SMB_COMMON_H + +// This provides SMB flow data and base class for SMB session data +// Also provides common functions used by both versions + +#include "file_api/file_api.h" +#include "main/snort_debug.h" +#include "protocols/packet.h" +#include "profiler/profiler_defs.h" + +#include "dce_common.h" +#include "dce_smb_module.h" +#include "smb_common.h" + +#define DCE2_SMB_NAME "dce_smb" +#define DCE2_SMB_HELP "dce over smb inspection" + +#define DCE2_SMB_ID 0xff534d42 /* \xffSMB */ +#define DCE2_SMB2_ID 0xfe534d42 /* \xfeSMB */ +#define DCE2_SMB_ID_SIZE 4 + +#define SMB_DEFAULT_MAX_CREDIT 8192 +#define SMB_DEFAULT_MEMCAP 8388608 +#define SMB_DEFAULT_MAX_COMPOUND_REQ 3 + +#define DCE2_SMB_RPKT_TYPE_MAX 4 +#define DCE2_SMB_RPKT_TYPE_START 1 + +#define DCE2_SMB_BAD_NBSS_TYPE 2 +#define DCE2_SMB_BAD_TYPE 3 +#define DCE2_SMB_BAD_ID 4 +#define DCE2_SMB_BAD_WCT 5 +#define DCE2_SMB_BAD_BCC 6 +#define DCE2_SMB_BAD_FORM 7 +#define DCE2_SMB_BAD_OFF 8 +#define DCE2_SMB_TDCNT_ZE 9 +#define DCE2_SMB_NB_LT_SMBHDR 10 +#define DCE2_SMB_NB_LT_COM 11 +#define DCE2_SMB_NB_LT_BCC 12 +#define DCE2_SMB_NB_LT_DSIZE 13 +#define DCE2_SMB_TDCNT_LT_DSIZE 14 +#define DCE2_SMB_DSENT_GT_TDCNT 15 +#define DCE2_SMB_BCC_LT_DSIZE 16 +#define DCE2_SMB_INVALID_DSIZE 17 +#define DCE2_SMB_EXCESSIVE_TREE_CONNECTS 18 +#define DCE2_SMB_EXCESSIVE_READS 19 +#define DCE2_SMB_EXCESSIVE_CHAINING 20 +#define DCE2_SMB_MULT_CHAIN_SS 21 +#define DCE2_SMB_MULT_CHAIN_TC 22 +#define DCE2_SMB_CHAIN_SS_LOGOFF 23 +#define DCE2_SMB_CHAIN_TC_TDIS 24 +#define DCE2_SMB_CHAIN_OPEN_CLOSE 25 +#define DCE2_SMB_INVALID_SHARE 26 + +#define DCE2_SMB_V1 44 +#define DCE2_SMB_V2 45 +#define DCE2_SMB_INVALID_BINDING 46 +#define DCE2_SMB2_EXCESSIVE_COMPOUNDING 47 +#define DCE2_SMB_DCNT_ZERO 48 +#define DCE2_SMB_DCNT_MISMATCH 49 +#define DCE2_SMB_MAX_REQS_EXCEEDED 50 +#define DCE2_SMB_REQS_SAME_MID 51 +#define DCE2_SMB_DEPR_DIALECT_NEGOTIATED 52 +#define DCE2_SMB_DEPR_COMMAND_USED 53 +#define DCE2_SMB_UNUSUAL_COMMAND_USED 54 +#define DCE2_SMB_INVALID_SETUP_COUNT 55 +#define DCE2_SMB_MULTIPLE_NEGOTIATIONS 56 +#define DCE2_SMB_EVASIVE_FILE_ATTRS 57 +#define DCE2_SMB_INVALID_FILE_OFFSET 58 +#define DCE2_SMB_BAD_NEXT_COMMAND_OFFSET 59 + +struct dce2SmbStats +{ + PegCount events; + + PegCount co_pdus; + PegCount co_bind; + PegCount co_bind_ack; + PegCount co_alter_ctx; + PegCount co_alter_ctx_resp; + PegCount co_bind_nack; + PegCount co_request; + PegCount co_response; + PegCount co_cancel; + PegCount co_orphaned; + PegCount co_fault; + PegCount co_auth3; + PegCount co_shutdown; + PegCount co_reject; + PegCount co_ms_pdu; + PegCount co_other_req; + PegCount co_other_resp; + PegCount co_req_fragments; + PegCount co_resp_fragments; + PegCount co_cli_max_frag_size; + PegCount co_cli_min_frag_size; + PegCount co_cli_seg_reassembled; + PegCount co_cli_frag_reassembled; + PegCount co_srv_max_frag_size; + PegCount co_srv_min_frag_size; + PegCount co_srv_seg_reassembled; + PegCount co_srv_frag_reassembled; + + PegCount smb_sessions; + PegCount smb_pkts; + PegCount smb_ignored_bytes; + PegCount smb_cli_seg_reassembled; + PegCount smb_srv_seg_reassembled; + PegCount smb_max_outstanding_requests; + // FIXIT-M more peg count foo + /*uint64_t smb_com_stats[2][SMB_MAX_NUM_COMS]; + uint64_t smb_chained_stats[2][SMB_ANDX_COM__MAX][SMB_MAX_NUM_COMS]; + // The +1 is for codes beyond the range of the highest valid subcommand code + // Indicates a bogus subcommand + uint64_t smb_trans_subcom_stats[2][TRANS_SUBCOM_MAX+1]; + uint64_t smb_trans2_subcom_stats[2][TRANS2_SUBCOM_MAX+1]; + uint64_t smb_nt_transact_subcom_stats[2][NT_TRANSACT_SUBCOM_MAX+1]; + */ + PegCount smb_files_processed; + /* SMB2 stats */ + 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_inv_str_sz; + PegCount v2_wrt_req_hdr_err; + PegCount v2_wrt_resp_hdr_err; + PegCount v2_read; + PegCount v2_read_err_resp; + 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_setinfo; + PegCount v2_stinf_err_resp; + 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_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_inv_file_ctx_err; + PegCount v2_msgs_uninspected; + PegCount v2_cmpnd_req_lt_crossed; + PegCount v2_tree_ignored; + PegCount v2_session_ignored; + PegCount concurrent_sessions; + PegCount max_concurrent_sessions; +}; + +enum DCE2_SmbVersion +{ + DCE2_SMB_VERSION_NULL, + DCE2_SMB_VERSION_1, + DCE2_SMB_VERSION_2 +}; + +enum Dce2SmbPduState +{ + DCE2_SMB_PDU_STATE__COMMAND, + DCE2_SMB_PDU_STATE__RAW_DATA +}; + +extern THREAD_LOCAL dce2SmbStats dce2_smb_stats; +extern THREAD_LOCAL snort::ProfileStats dce2_smb_pstat_main; + +class Dce2SmbSessionData +{ +public: + Dce2SmbSessionData() = delete; + Dce2SmbSessionData(const snort::Packet*, const dce2SmbProtoConf*); + virtual ~Dce2SmbSessionData() { } + + virtual void process() = 0; + virtual void handle_retransmit(FilePosition, FileVerdict) = 0; + + DCE2_SsnData* get_dce2_session_data() + { return &sd; } + + snort::Flow* get_flow() + { return tcp_flow; } + + int64_t get_max_file_depth() + { return max_file_depth; } + + uint16_t get_max_outstanding_requests() + { + return sd.config ? ((dce2SmbProtoConf*)sd.config)->smb_max_credit : + SMB_DEFAULT_MAX_CREDIT; + } + + int64_t get_smb_file_depth() + { + return ((dce2SmbProtoConf*)sd.config)->smb_file_depth; + } + + uint16_t get_smb_max_compound() + { + return sd.config ? ((dce2SmbProtoConf*)sd.config)->smb_max_compound : + SMB_DEFAULT_MAX_COMPOUND_REQ; + } + +protected: + DCE2_SsnData sd; + DCE2_Policy policy; + int64_t max_file_depth; + int dialect_index; + snort::Flow* tcp_flow; +}; + +class Dce2SmbFlowData : public snort::FlowData +{ +public: + Dce2SmbFlowData(Dce2SmbSessionData*); + ~Dce2SmbFlowData() override; + + static void init() + { inspector_id = snort::FlowData::create_flow_data_id(); } + + size_t size_of() override + { return sizeof(*this); } + + Dce2SmbSessionData* get_smb_session_data() + { return ssd; } + + Dce2SmbSessionData* upgrade(const snort::Packet*); + void handle_retransmit(snort::Packet*) override; + +public: + static unsigned inspector_id; + +private: + Dce2SmbSessionData* ssd; +}; + +Dce2SmbSessionData* create_new_smb_session(const snort::Packet*, dce2SmbProtoConf*); +DCE2_SsnData* get_dce2_session_data(snort::Flow*); +snort::FileContext* get_smb_file_context(const snort::Packet*); +snort::FileContext* get_smb_file_context(uint64_t, uint64_t, bool); +char* get_smb_file_name(const uint8_t*, uint32_t, bool, uint16_t*); + +#endif + diff --git a/src/service_inspectors/dce_rpc/dce_smb_inspector.cc b/src/service_inspectors/dce_rpc/dce_smb_inspector.cc new file mode 100644 index 000000000..fdcabbf75 --- /dev/null +++ b/src/service_inspectors/dce_rpc/dce_smb_inspector.cc @@ -0,0 +1,181 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2021 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_smb_inspector.h author Dipta Pandit + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "dce_smb_inspector.h" + +#include "dce_smb_common.h" +#include "dce_smb_module.h" +#include "dce_smb_utils.h" +#include "dce_smb2_session_cache.h" + +using namespace snort; + +Dce2Smb::Dce2Smb(const dce2SmbProtoConf& pc) +{ + config = pc; +} + +Dce2Smb::~Dce2Smb() +{ + if (config.smb_invalid_shares) + { + DCE2_ListDestroy(config.smb_invalid_shares); + } +} + +void Dce2Smb::show(const SnortConfig*) const +{ + print_dce2_smb_conf(config); +} + +void Dce2Smb::eval(Packet* p) +{ + Profile profile(dce2_smb_pstat_main); + + assert(p->has_tcp_data()); + assert(p->flow); + + Dce2SmbFlowData* smb_flowdata = + (Dce2SmbFlowData*)p->flow->get_flow_data(Dce2SmbFlowData::inspector_id); + + Dce2SmbSessionData* smb_session_data; + if (smb_flowdata) + smb_session_data = smb_flowdata->get_smb_session_data(); + else + smb_session_data = create_new_smb_session(p, &config); + + if (smb_session_data) + { + dce2_detected = 0; + p->packet_flags |= PKT_ALLOW_MULTIPLE_DETECT; + p->endianness = new DceEndianness(); + + smb_session_data->process(); + + //smb_session_data may not be valid anymore in case of upgrade + //but flow will always have updated session + if (!dce2_detected) + DCE2_Detect(get_dce2_session_data(p->flow)); + + delete(p->endianness); + p->endianness = nullptr; + } + else + debug_logf(dce_smb_trace, p, "non-smb packet detected\n"); +} + +void Dce2Smb::clear(Packet* p) +{ + DCE2_SsnData* sd = get_dce2_session_data(p->flow); + if (sd) + DCE2_ResetRopts(sd, p); +} + +//------------------------------------------------------------------------- +// api stuff +//------------------------------------------------------------------------- + +size_t session_cache_size; + +static Module* mod_ctor() +{ + return new Dce2SmbModule; +} + +static void mod_dtor(Module* m) +{ + delete m; +} + +static void dce2_smb_init() +{ + Dce2SmbFlowData::init(); + DCE2_SmbInitGlobals(); + DCE2_SmbInitDeletePdu(); + 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 size_t get_max_smb_session(dce2SmbProtoConf* config) +{ + size_t smb_sess_storage_req = (sizeof(Dce2Smb2SessionTracker) + + sizeof(Dce2Smb2TreeTracker) + sizeof(Dce2Smb2RequestTracker) + + (sizeof(Dce2Smb2FileTracker) * SMB_AVG_FILES_PER_SESSION)); + + size_t max_smb_mem = DCE2_ScSmbMemcap(config); + + return (max_smb_mem/smb_sess_storage_req); +} + +static Inspector* dce2_smb_ctor(Module* m) +{ + Dce2SmbModule* mod = (Dce2SmbModule*)m; + dce2SmbProtoConf config; + mod->get_data(config); + session_cache_size = get_max_smb_session(&config); + return new Dce2Smb(config); +} + +static void dce2_smb_dtor(Inspector* p) +{ + delete p; +} + +const InspectApi dce2_smb_api = +{ + { + PT_INSPECTOR, + sizeof(InspectApi), + INSAPI_VERSION, + 0, + API_RESERVED, + API_OPTIONS, + DCE2_SMB_NAME, + DCE2_SMB_HELP, + mod_ctor, + mod_dtor + }, + IT_SERVICE, + PROTO_BIT__PDU, + nullptr, // buffers + "netbios-ssn", + dce2_smb_init, + nullptr, // pterm + dce2_smb_thread_int, // tinit + dce_smb_thread_term, // tterm + dce2_smb_ctor, + dce2_smb_dtor, + nullptr, // ssn + nullptr // reset +}; + diff --git a/src/service_inspectors/dce_rpc/dce_smb_inspector.h b/src/service_inspectors/dce_rpc/dce_smb_inspector.h new file mode 100644 index 000000000..fb1c8dd2e --- /dev/null +++ b/src/service_inspectors/dce_rpc/dce_smb_inspector.h @@ -0,0 +1,50 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2021 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_smb_inspector.h author Dipta Pandit + +#ifndef DCE_SMB_INSPECTOR_H +#define DCE_SMB_INSPECTOR_H + +#include "managers/inspector_manager.h" + +#include "dce_smb_module.h" +#include "dce_smb_paf.h" + +class Dce2Smb : public snort::Inspector +{ +public: + Dce2Smb(const dce2SmbProtoConf&); + ~Dce2Smb() override; + + void show(const snort::SnortConfig*) const override; + void eval(snort::Packet*) override; + void clear(snort::Packet*) override; + + snort::StreamSplitter* get_splitter(bool c2s) override + { return new Dce2SmbSplitter(c2s); } + + bool can_carve_files() const override + { return true; } + +private: + dce2SmbProtoConf config; +}; + +#endif + diff --git a/src/service_inspectors/dce_rpc/dce_smb_module.cc b/src/service_inspectors/dce_rpc/dce_smb_module.cc index 59785958c..842f49de9 100644 --- a/src/service_inspectors/dce_rpc/dce_smb_module.cc +++ b/src/service_inspectors/dce_rpc/dce_smb_module.cc @@ -29,7 +29,7 @@ #include "trace/trace.h" #include "utils/util.h" -#include "dce_smb.h" +#include "dce_smb_common.h" using namespace snort; using namespace std; @@ -84,7 +84,8 @@ static const PegInfo dce2_smb_pegs[] = { CountType::SUM, "max_outstanding_requests", "total smb maximum outstanding requests" }, { CountType::SUM, "files_processed", "total smb files processed" }, { 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_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", @@ -99,15 +100,16 @@ static const PegInfo dce2_smb_pegs[] = { 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_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" }, + "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" }, + "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", @@ -115,17 +117,17 @@ static const PegInfo dce2_smb_pegs[] = { 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_err_resp", + "total number of SMBv2 write error response packets seen" }, { 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_wrt_resp_hdr_err", + "total number of SMBv2 write response 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_err_resp", + "total number of SMBv2 read error response packets seen" }, { CountType::SUM, "v2_read_inv_str_sz", "total number of SMBv2 read packets seen with invalid structure size" }, { CountType::SUM, "v2_read_rtrkr_misng", @@ -134,10 +136,9 @@ static const PegInfo dce2_smb_pegs[] = "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_setinfo", "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_inv_str_sz", "total number of SMBv2 set info packets seen with invalid structure size" }, { CountType::SUM, "v2_stinf_req_ftrkr_misng", @@ -145,9 +146,8 @@ static const PegInfo dce2_smb_pegs[] = { 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_err_resp", + "total number of SMBv2 close error response packets seen" }, { 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", @@ -168,14 +168,16 @@ static const PegInfo dce2_smb_pegs[] = { 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::SUM, "v2_tree_ignored", + "total number of packets ignored due to missing tree tracker" }, + { CountType::SUM, "v2_session_ignored", + "total number of packets ignored due to missing session tracker" }, { CountType::NOW, "concurrent_sessions", "total concurrent sessions" }, { CountType::MAX, "max_concurrent_sessions", "maximum concurrent sessions" }, { CountType::END, nullptr, nullptr } @@ -536,6 +538,5 @@ void print_dce2_smb_conf(const dce2SmbProtoConf& config) 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 3d7b0be19..60c72b652 100644 --- a/src/service_inspectors/dce_rpc/dce_smb_module.h +++ b/src/service_inspectors/dce_rpc/dce_smb_module.h @@ -21,8 +21,9 @@ #ifndef DCE_SMB_MODULE_H #define DCE_SMB_MODULE_H -#include "dce_common.h" #include "framework/module.h" + +#include "dce_common.h" #include "dce_list.h" namespace snort @@ -36,6 +37,59 @@ extern THREAD_LOCAL const snort::Trace* dce_smb_trace; #define DCE2_VALID_SMB_VERSION_FLAG_V1 1 #define DCE2_VALID_SMB_VERSION_FLAG_V2 2 +#define DCE2_SMB_BAD_NBSS_TYPE_STR "SMB - bad NetBIOS session service session type" +#define DCE2_SMB_BAD_TYPE_STR "SMB - bad SMB message type" +#define DCE2_SMB_BAD_ID_STR "SMB - bad SMB Id (not \\xffSMB for SMB1 or not \\xfeSMB for SMB2)" +#define DCE2_SMB_BAD_WCT_STR "SMB - bad word count or structure size" +#define DCE2_SMB_BAD_BCC_STR "SMB - bad byte count" +#define DCE2_SMB_BAD_FORM_STR "SMB - bad format type" +#define DCE2_SMB_BAD_OFF_STR "SMB - bad offset" +#define DCE2_SMB_TDCNT_ZE_STR "SMB - zero total data count" +#define DCE2_SMB_NB_LT_SMBHDR_STR "SMB - NetBIOS data length less than SMB header length" +#define DCE2_SMB_NB_LT_COM_STR "SMB - remaining NetBIOS data length less than command length" +#define DCE2_SMB_NB_LT_BCC_STR "SMB - remaining NetBIOS data length less than command byte count" +#define DCE2_SMB_NB_LT_DSIZE_STR \ + "SMB - remaining NetBIOS data length less than command data size" +#define DCE2_SMB_TDCNT_LT_DSIZE_STR \ + "SMB - remaining total data count less than this command data size" +#define DCE2_SMB_DSENT_GT_TDCNT_STR \ + "SMB - total data sent (STDu64) greater than command total data expected" +#define DCE2_SMB_BCC_LT_DSIZE_STR "SMB - byte count less than command data size (STDu64)" +#define DCE2_SMB_INVALID_DSIZE_STR "SMB - invalid command data size for byte count" +#define DCE2_SMB_EXCESSIVE_TREE_CONNECTS_STR \ + "SMB - excessive tree connect requests with pending tree connect responses" +#define DCE2_SMB_EXCESSIVE_READS_STR "SMB - excessive read requests with pending read responses" +#define DCE2_SMB_EXCESSIVE_CHAINING_STR "SMB - excessive command chaining" +#define DCE2_SMB_MULT_CHAIN_SS_STR "SMB - multiple chained tree connect requests" +#define DCE2_SMB_MULT_CHAIN_TC_STR "SMB - multiple chained tree connect requests" +#define DCE2_SMB_CHAIN_SS_LOGOFF_STR "SMB - chained/compounded login followed by logoff" +#define DCE2_SMB_CHAIN_TC_TDIS_STR \ + "SMB - chained/compounded tree connect followed by tree disconnect" +#define DCE2_SMB_CHAIN_OPEN_CLOSE_STR \ + "SMB - chained/compounded open pipe followed by close pipe" +#define DCE2_SMB_INVALID_SHARE_STR "SMB - invalid share access" + +#define DCE2_SMB_V1_STR "SMB - invalid SMB version 1 seen" +#define DCE2_SMB_V2_STR "SMB - invalid SMB version 2 seen" +#define DCE2_SMB_INVALID_BINDING_STR "SMB - invalid user, tree connect, file binding" +#define DCE2_SMB2_EXCESSIVE_COMPOUNDING_STR "SMB - excessive command compounding" +#define DCE2_SMB_DCNT_ZERO_STR "SMB - zero data count" +#define DCE2_SMB_DCNT_MISMATCH_STR "SMB - data count mismatch in command and format" +#define DCE2_SMB_MAX_REQS_EXCEEDED_STR "SMB - maximum number of outstanding requests exceeded" +#define DCE2_SMB_REQS_SAME_MID_STR "SMB - outstanding requests with same MID" +#define DCE2_SMB_DEPR_DIALECT_NEGOTIATED_STR "SMB - deprecated dialect negotiated" +#define DCE2_SMB_DEPR_COMMAND_USED_STR "SMB - deprecated command used" +#define DCE2_SMB_UNUSUAL_COMMAND_USED_STR "SMB - unusual command used" +#define DCE2_SMB_INVALID_SETUP_COUNT_STR "SMB - invalid setup count for command" +#define DCE2_SMB_MULTIPLE_NEGOTIATIONS_STR \ + "SMB - client attempted multiple dialect negotiations on session" +#define DCE2_SMB_EVASIVE_FILE_ATTRS_STR \ + "SMB - client attempted to create or set a file's attributes to readonly/hidden/system" +#define DCE2_SMB_INVALID_FILE_OFFSET_STR \ + "SMB - file offset provided is greater than file size specified" +#define DCE2_SMB_BAD_NEXT_COMMAND_OFFSET_STR \ + "SMB - next command specified in SMB2 header is beyond payload boundary" + enum dce2SmbFingerprintPolicy { DCE2_SMB_FINGERPRINT_POLICY_NONE = 0, diff --git a/src/service_inspectors/dce_rpc/dce_smb_paf.cc b/src/service_inspectors/dce_rpc/dce_smb_paf.cc index 12d2250ab..26758cc2c 100644 --- a/src/service_inspectors/dce_rpc/dce_smb_paf.cc +++ b/src/service_inspectors/dce_rpc/dce_smb_paf.cc @@ -25,7 +25,7 @@ #include "dce_smb_paf.h" -#include "dce_smb.h" +#include "dce_smb_common.h" namespace { @@ -45,8 +45,8 @@ using namespace snort; * junk states, header type must be Session Message. * *********************************************************************/ -static inline bool DCE2_PafSmbIsValidNetbiosHdr(uint32_t nb_hdr, bool junk, const SmbNtHdr* nt_hdr, - uint32_t* nb_len) +static inline bool DCE2_PafSmbIsValidNetbiosHdr(uint32_t nb_hdr, bool junk, + const SmbNtHdr* nt_hdr, uint32_t* nb_len) { uint8_t type = (uint8_t)(nb_hdr >> 24); uint8_t bit = (uint8_t)((nb_hdr & 0x00ff0000) >> 16); @@ -78,7 +78,7 @@ static inline bool DCE2_PafSmbIsValidNetbiosHdr(uint32_t nb_hdr, bool junk, cons //See [MS-SMB] 2.1 Transport. There is no such limit for SMB2 or SMB3 if (smb_id == DCE2_SMB_ID) { - if ((bit != 0x00) && (bit != 0x01)) + if ((bit != 0x00) and (bit != 0x01)) return false; } nbs_hdr = htonl(nb_hdr); @@ -106,8 +106,8 @@ static inline bool DCE2_PafSmbIsValidNetbiosHdr(uint32_t nb_hdr, bool junk, cons * state 7 until this is the case. * *********************************************************************/ -static StreamSplitter::Status dce2_smb_paf(DCE2_PafSmbData* ss, Flow* flow, const uint8_t* data, - uint32_t len, uint32_t, uint32_t* fp) +static StreamSplitter::Status dce2_smb_paf(DCE2_PafSmbData* ss, Flow* flow, + const uint8_t* data, uint32_t len, uint32_t, uint32_t* fp) { uint32_t n = 0; StreamSplitter::Status ps = StreamSplitter::SEARCH; diff --git a/src/service_inspectors/dce_rpc/dce_smb_transaction.cc b/src/service_inspectors/dce_rpc/dce_smb_transaction.cc index 8652394ae..427da039e 100644 --- a/src/service_inspectors/dce_rpc/dce_smb_transaction.cc +++ b/src/service_inspectors/dce_rpc/dce_smb_transaction.cc @@ -290,7 +290,7 @@ static DCE2_Ret DCE2_SmbNtTransactCreateReq(DCE2_SmbSsnData* ssd, dce2_move(param_ptr, param_len, pad); ssd->cur_rtracker->file_name = - DCE2_SmbGetFileName(param_ptr, file_name_length, unicode, &ssd->cur_rtracker->file_name_size); + get_smb_file_name(param_ptr, file_name_length, unicode, &ssd->cur_rtracker->file_name_size); return DCE2_RET__SUCCESS; } @@ -958,7 +958,7 @@ static DCE2_Ret DCE2_SmbTrans2Open2Req(DCE2_SmbSsnData* ssd, dce2_move(param_ptr, param_len, sizeof(SmbTrans2Open2ReqParams)); ssd->cur_rtracker->file_name = - DCE2_SmbGetFileName(param_ptr, param_len, unicode, &ssd->cur_rtracker->file_name_size); + get_smb_file_name(param_ptr, param_len, unicode, &ssd->cur_rtracker->file_name_size); return DCE2_RET__SUCCESS; } diff --git a/src/service_inspectors/dce_rpc/dce_smb_transaction_utils.h b/src/service_inspectors/dce_rpc/dce_smb_transaction_utils.h index db534f806..fe528acad 100644 --- a/src/service_inspectors/dce_rpc/dce_smb_transaction_utils.h +++ b/src/service_inspectors/dce_rpc/dce_smb_transaction_utils.h @@ -22,7 +22,7 @@ #ifndef DCE_SMB_TRANSACTION_UTILS_H #define DCE_SMB_TRANSACTION_UTILS_H -#include "dce_smb.h" +#include "dce_smb1.h" DCE2_Ret DCE2_SmbTransactionGetName(const uint8_t* nb_ptr, uint32_t nb_len, uint16_t bcc, bool unicode); diff --git a/src/service_inspectors/dce_rpc/dce_smb_utils.cc b/src/service_inspectors/dce_rpc/dce_smb_utils.cc index d253ee4b5..a5fab9775 100644 --- a/src/service_inspectors/dce_rpc/dce_smb_utils.cc +++ b/src/service_inspectors/dce_rpc/dce_smb_utils.cc @@ -94,47 +94,6 @@ bool DCE2_SmbIsTidIPC(DCE2_SmbSsnData* ssd, const uint16_t tid) return false; } -// Extract file name from data. Supports ASCII and UTF-16LE. -// Returns byte stream (ASCII or UTF-16LE with BOM) -char* DCE2_SmbGetFileName(const uint8_t* data, uint32_t data_len, bool unicode, - uint16_t* file_name_len) -{ - const uint8_t inc = unicode ? 2 : 1; - if (data_len < inc) - return nullptr; - - const uint32_t max_len = unicode ? data_len - 1 : data_len; - // Move forward. Don't know if the end of data is actually - // the end of the string. - uint32_t i; - for (i = 0; i < max_len; i += inc) - { - uint16_t uchar = unicode ? extract_16bits(data + i) : data[i]; - if (uchar == 0) - break; - } - - char* fname = nullptr; - const uint32_t real_len = i; - - if (unicode) - { - fname = (char*)snort_calloc(real_len + UTF_16_LE_BOM_LEN + 2); - memcpy(fname, UTF_16_LE_BOM, UTF_16_LE_BOM_LEN);//Prepend with BOM - memcpy(fname + UTF_16_LE_BOM_LEN, data, real_len); - *file_name_len = real_len + UTF_16_LE_BOM_LEN; - } - else - { - fname = (char*)snort_alloc(real_len + 1); - memcpy(fname, data, real_len); - fname[real_len] = 0; - *file_name_len = real_len; - } - - return fname; -} - int DCE2_SmbUidTidFidCompare(const void* a, const void* b) { int x = (int)(uintptr_t)a; @@ -1797,9 +1756,10 @@ void DCE2_SmbProcessFileData(DCE2_SmbSsnData* ssd, } else if (ftracker->ff_file_offset < ftracker->ff_bytes_processed) { - debug_logf(dce_smb_trace, nullptr, "File offset %" PRIu64 " is " - "less than bytes processed %" PRIu64 " - aborting.\n", - ftracker->ff_file_offset, ftracker->ff_bytes_processed); + debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), + "File offset %" PRIu64 " is less than bytes processed %" + PRIu64 " - aborting.\n", ftracker->ff_file_offset, + ftracker->ff_bytes_processed); DCE2_SmbAbortFileAPI(ssd); DCE2_SmbSetNewFileAPIFileTracker(ssd); diff --git a/src/service_inspectors/dce_rpc/dce_smb_utils.h b/src/service_inspectors/dce_rpc/dce_smb_utils.h index d9834a69a..4f947bf31 100644 --- a/src/service_inspectors/dce_rpc/dce_smb_utils.h +++ b/src/service_inspectors/dce_rpc/dce_smb_utils.h @@ -22,7 +22,7 @@ #ifndef DCE_SMB_UTILS_H #define DCE_SMB_UTILS_H -#include "dce_smb.h" +#include "dce_smb1.h" #include "file_api/file_flows.h" /******************************************************************** @@ -125,7 +125,6 @@ inline bool DCE2_ComInfoCanProcessCommand(const DCE2_SmbComInfo* com_info) * Function prototypes ********************************************************************/ bool DCE2_SmbIsTidIPC(DCE2_SmbSsnData*, const uint16_t); -char* DCE2_SmbGetFileName(const uint8_t *data, uint32_t data_len, bool unicode, uint16_t *file_name_len); int DCE2_SmbUidTidFidCompare(const void*, const void*); DCE2_Ret DCE2_SmbFindUid(DCE2_SmbSsnData*, const uint16_t); void DCE2_SmbInsertUid(DCE2_SmbSsnData*, const uint16_t); @@ -178,6 +177,7 @@ void DCE2_SmbProcessFileData(DCE2_SmbSsnData* ssd, uint32_t data_len, bool upload); void DCE2_FileDetect(); FileVerdict DCE2_get_file_verdict(); +void DCE2_SmbInitGlobals(); void DCE2_SmbInitDeletePdu(); void DCE2_Update_Ftracker_from_ReqTracker(DCE2_SmbFileTracker*, DCE2_SmbRequestTracker*); diff --git a/src/service_inspectors/dce_rpc/dev_notes.txt b/src/service_inspectors/dce_rpc/dev_notes.txt index 7a1524640..6942429e4 100644 --- a/src/service_inspectors/dce_rpc/dev_notes.txt +++ b/src/service_inspectors/dce_rpc/dev_notes.txt @@ -27,5 +27,16 @@ 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. +For SMBv2, it supports TCP as transport and it also supports multiple File transfer +on single SMB2 connection. +The SMB inspector implements base class for smb flow data, +which all versions now inherits and implements. SMBv1 and SMBv2 code is completely +segregated, maintaining common code in separate file to reduce redundancy. +The common functionality used by both versions are in dce_smb_common files. +SMBv2 specific trackers are in there own file now. + +The inspector eval function checks for the presence of SMB flow data in the flow, +if not present, it creates an SMB flow data according to detected version, otherwise +calls the respective process function. +The process functions processes the commands according to respective versions and when +file transfer is detected, it calls file_flow process(). \ No newline at end of file diff --git a/src/service_inspectors/dce_rpc/smb_message.cc b/src/service_inspectors/dce_rpc/smb_message.cc index d8da1ca69..31246362e 100644 --- a/src/service_inspectors/dce_rpc/smb_message.cc +++ b/src/service_inspectors/dce_rpc/smb_message.cc @@ -28,7 +28,6 @@ #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" @@ -1032,6 +1031,8 @@ static DCE2_SmbRequestTracker* DCE2_SmbInspect(DCE2_SmbSsnData* ssd, const SmbNt { int smb_com = SmbCom(smb_hdr); + if (smb_com < 0 or smb_com > 255) return nullptr; + debug_logf(dce_smb_trace, DetectionEngine::get_current_packet(), "SMB command: %s (0x%02X)\n", get_smb_com_string(smb_com), smb_com); @@ -1293,74 +1294,6 @@ void DCE2_SmbDataFree(DCE2_SmbSsnData* ssd) } } -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; - memory::MemoryCap::update_allocations(sizeof(*this)); -} - -Dce2SmbFlowData::~Dce2SmbFlowData() -{ - if (DCE2_SMB_VERSION_1 == smb_version) - { - DCE2_SmbDataFree((DCE2_SmbSsnData*)dce2_smb_session_data); - delete (DCE2_SmbSsnData*)dce2_smb_session_data; - } - else - { - DCE2_Smb2RemoveAllSession((DCE2_Smb2SsnData*)dce2_smb_session_data); - delete (DCE2_Smb2SsnData*)dce2_smb_session_data; - memory::MemoryCap::update_deallocations(sizeof(*(DCE2_Smb2SsnData*)dce2_smb_session_data)); - } - assert(dce2_smb_stats.concurrent_sessions > 0); - dce2_smb_stats.concurrent_sessions--; - memory::MemoryCap::update_deallocations(sizeof(*this)); -} - -unsigned Dce2SmbFlowData::inspector_id = 0; - -static inline DCE2_SmbSsnData* set_new_dce2_smb_session(Packet* p) -{ - Dce2SmbFlowData* fd = new Dce2SmbFlowData; - fd->smb_version = DCE2_SMB_VERSION_1; - fd->dce2_smb_session_data = new DCE2_SmbSsnData(); - p->flow->set_flow_data(fd); - return (DCE2_SmbSsnData*)(fd->dce2_smb_session_data); -} - -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 ) - { - dce2_smb_sess->dialect_index = DCE2_SENTINEL; - dce2_smb_sess->max_outstanding_requests = 10; // Until Negotiate/SessionSetupAndX - dce2_smb_sess->cli_data_state = DCE2_SMB_DATA_STATE__NETBIOS_HEADER; - dce2_smb_sess->srv_data_state = DCE2_SMB_DATA_STATE__NETBIOS_HEADER; - dce2_smb_sess->pdu_state = DCE2_SMB_PDU_STATE__COMMAND; - dce2_smb_sess->uid = DCE2_SENTINEL; - dce2_smb_sess->tid = DCE2_SENTINEL; - dce2_smb_sess->ftracker.fid_v1 = DCE2_SENTINEL; - dce2_smb_sess->rtracker.mid = DCE2_SENTINEL; - dce2_smb_sess->max_file_depth = FileService::get_max_file_depth(); - - DCE2_ResetRopts(&dce2_smb_sess->sd, p); - - dce2_smb_stats.smb_sessions++; - - dce2_smb_sess->sd.trans = DCE2_TRANS_TYPE__SMB; - dce2_smb_sess->sd.server_policy = config->common.policy; - dce2_smb_sess->sd.client_policy = DCE2_POLICY__WINXP; - dce2_smb_sess->sd.config = (void*)config; - } - - return dce2_smb_sess; -} - /******************************************************************** * Function: DCE2_NbssHdrChecks() * @@ -1622,14 +1555,13 @@ void DCE2_Smb1Process(DCE2_SmbSsnData* ssd) { // Upgrade connection to SMBv2 debug_log(dce_smb_trace, p, "upgrading to smb2 session\n"); - 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); + if (fd) + { + Dce2SmbSessionData* dce2_smb2_sess = fd->upgrade(p); + dce2_smb2_sess->process(); + } } else ssd->sd.flags |= DCE2_SSN_FLAG__SMB2; @@ -2554,49 +2486,3 @@ void DCE2_SmbInitGlobals() } } -/* smb2 Functions */ - -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(); - memory::MemoryCap::update_allocations(sizeof(*(DCE2_Smb2SsnData*)(fd->dce2_smb_session_data))); - DCE2_Smb2InitData((DCE2_Smb2SsnData*)fd->dce2_smb_session_data); - p->flow->set_flow_data(fd); - return((DCE2_Smb2SsnData*)fd->dce2_smb_session_data); -} - -DCE2_Smb2SsnData* dce2_create_new_smb2_session(Packet* p, dce2SmbProtoConf* config) -{ - DCE2_Smb2SsnData* dce2_smb2_sess = set_new_dce2_smb2_session(p); - if ( dce2_smb2_sess ) - { - dce2_smb2_sess->sd.flags |= DCE2_SSN_FLAG__SMB2; - dce2_smb2_sess->dialect_index = DCE2_SENTINEL; - - 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); - } - - 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 019e63f7d..0956b7158 100644 --- a/src/service_inspectors/dce_rpc/smb_message.h +++ b/src/service_inspectors/dce_rpc/smb_message.h @@ -2190,10 +2190,7 @@ inline uint16_t SmbWriteAndCloseRespCount(const SmbWriteAndCloseResp* resp) #pragma pack() -void DCE2_SmbInitGlobals(); 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