uint16_t cmd_size;
};
+// Inline accessor functions for DCE2_SmbComInfo
+
+static inline bool DCE2_ComInfoIsResponse(const DCE2_SmbComInfo* com_info)
+{
+ return (com_info->smb_type == SMB_TYPE__RESPONSE) ? true : false;
+}
+
+static inline bool DCE2_ComInfoIsRequest(const DCE2_SmbComInfo* com_info)
+{
+ return (com_info->smb_type == SMB_TYPE__REQUEST) ? true : false;
+}
+
+static inline uint16_t DCE2_ComInfoByteCount(const DCE2_SmbComInfo* com_info)
+{
+ return com_info->byte_count;
+}
+
+static inline uint8_t DCE2_ComInfoSmbCom(const DCE2_SmbComInfo* com_info)
+{
+ return com_info->smb_com;
+}
+
+static inline uint16_t DCE2_ComInfoCommandSize(const DCE2_SmbComInfo* com_info)
+{
+ return com_info->cmd_size;
+}
+
+static inline bool DCE2_ComInfoIsCommandOK(const DCE2_SmbComInfo* com_info)
+{
+ return (com_info->cmd_error == DCE2_SMB_COM_ERROR__COMMAND_OK) ? true : false;
+}
+
+static inline bool DCE2_ComInfoIsStatusError(const DCE2_SmbComInfo* com_info)
+{
+ return (com_info->cmd_error & DCE2_SMB_COM_ERROR__STATUS_ERROR) ? true : false;
+}
+
+static inline bool DCE2_ComInfoIsInvalidWordCount(const DCE2_SmbComInfo* com_info)
+{
+ return (com_info->cmd_error & DCE2_SMB_COM_ERROR__INVALID_WORD_COUNT) ? true : false;
+}
+
+static inline bool DCE2_ComInfoIsInvalidByteCount(const DCE2_SmbComInfo* com_info)
+{
+ return (com_info->cmd_error & DCE2_SMB_COM_ERROR__INVALID_BYTE_COUNT) ? true : false;
+}
+
+static inline bool DCE2_ComInfoIsBadLength(const DCE2_SmbComInfo* com_info)
+{
+ return (com_info->cmd_error & DCE2_SMB_COM_ERROR__BAD_LENGTH) ? true : false;
+}
+
+static inline uint8_t DCE2_ComInfoWordCount(const DCE2_SmbComInfo* com_info)
+{
+ return com_info->word_count;
+}
+
+// If this returns false, the command should not be processed
+static inline bool DCE2_ComInfoCanProcessCommand(const DCE2_SmbComInfo* com_info)
+{
+ if (DCE2_ComInfoIsBadLength(com_info)
+ || DCE2_ComInfoIsStatusError(com_info)
+ || DCE2_ComInfoIsInvalidWordCount(com_info))
+ return false;
+ return true;
+}
+
/********************************************************************
* Global variables
********************************************************************/
"No AndX Command" // 0xFF
};
+const char* smb_transaction_sub_command_strings[TRANS_SUBCOM_MAX] =
+{
+ "Unknown", // 0x0000
+ "TRANS_SET_NMPIPE_STATE", // 0x0001
+ "Unknown", // 0x0002
+ "Unknown", // 0x0003
+ "Unknown", // 0x0004
+ "Unknown", // 0x0005
+ "Unknown", // 0x0006
+ "Unknown", // 0x0007
+ "Unknown", // 0x0008
+ "Unknown", // 0x0009
+ "Unknown", // 0x000A
+ "Unknown", // 0x000B
+ "Unknown", // 0x000C
+ "Unknown", // 0x000D
+ "Unknown", // 0x000E
+ "Unknown", // 0x000F
+ "Unknown", // 0x0010
+ "TRANS_RAW_READ_NMPIPE", // 0x0011
+ "Unknown", // 0x0012
+ "Unknown", // 0x0013
+ "Unknown", // 0x0014
+ "Unknown", // 0x0015
+ "Unknown", // 0x0016
+ "Unknown", // 0x0017
+ "Unknown", // 0x0018
+ "Unknown", // 0x0019
+ "Unknown", // 0x001A
+ "Unknown", // 0x001B
+ "Unknown", // 0x001C
+ "Unknown", // 0x001D
+ "Unknown", // 0x001E
+ "Unknown", // 0x001F
+ "Unknown", // 0x0020
+ "TRANS_QUERY_NMPIPE_STATE", // 0x0021
+ "TRANS_QUERY_NMPIPE_INFO", // 0x0022
+ "TRANS_PEEK_NMPIPE", // 0x0023
+ "Unknown", // 0x0024
+ "Unknown", // 0x0025
+ "TRANS_TRANSACT_NMPIPE", // 0x0026
+ "Unknown", // 0x0027
+ "Unknown", // 0x0028
+ "Unknown", // 0x0029
+ "Unknown", // 0x002A
+ "Unknown", // 0x002B
+ "Unknown", // 0x002C
+ "Unknown", // 0x002D
+ "Unknown", // 0x002E
+ "Unknown", // 0x002F
+ "Unknown", // 0x0030
+ "TRANS_RAW_WRITE_NMPIPE", // 0x0031
+ "Unknown", // 0x0032
+ "Unknown", // 0x0033
+ "Unknown", // 0x0034
+ "Unknown", // 0x0035
+ "TRANS_READ_NMPIPE", // 0x0036
+ "TRANS_WRITE_NMPIPE", // 0x0037
+ "Unknown", // 0x0038
+ "Unknown", // 0x0039
+ "Unknown", // 0x003A
+ "Unknown", // 0x003B
+ "Unknown", // 0x003C
+ "Unknown", // 0x003D
+ "Unknown", // 0x003E
+ "Unknown", // 0x003F
+ "Unknown", // 0x0040
+ "Unknown", // 0x0041
+ "Unknown", // 0x0042
+ "Unknown", // 0x0043
+ "Unknown", // 0x0044
+ "Unknown", // 0x0045
+ "Unknown", // 0x0046
+ "Unknown", // 0x0047
+ "Unknown", // 0x0048
+ "Unknown", // 0x0049
+ "Unknown", // 0x004A
+ "Unknown", // 0x004B
+ "Unknown", // 0x004C
+ "Unknown", // 0x004D
+ "Unknown", // 0x004E
+ "Unknown", // 0x004F
+ "Unknown", // 0x0050
+ "Unknown", // 0x0051
+ "Unknown", // 0x0052
+ "TRANS_WAIT_NMPIPE", // 0x0053
+ "TRANS_CALL_NMPIPE" // 0x0054
+};
+
/********************************************************************
* Private function prototypes
********************************************************************/
const SmbNtHdr*);
static void DCE2_SmbCleanTransactionTracker(DCE2_SmbTransactionTracker*);
static void DCE2_SmbCleanRequestTracker(DCE2_SmbRequestTracker*);
+static void DCE2_SmbRemoveRequestTracker(DCE2_SmbSsnData*, DCE2_SmbRequestTracker*);
+static DCE2_SmbFileTracker* DCE2_SmbNewFileTracker(DCE2_SmbSsnData*,
+ const uint16_t, const uint16_t, const uint16_t);
+static DCE2_SmbFileTracker* DCE2_SmbGetFileTracker(DCE2_SmbSsnData*,
+ const uint16_t);
+static DCE2_SmbFileTracker* DCE2_SmbGetTmpFileTracker(DCE2_SmbRequestTracker*);
+static void DCE2_SmbRemoveFileTracker(DCE2_SmbSsnData*, DCE2_SmbFileTracker*);
+static void DCE2_SmbCleanFileTracker(DCE2_SmbFileTracker*);
+static int DCE2_SmbUidTidFidCompare(const void*, const void*);
+static DCE2_Ret DCE2_SmbFindUid(DCE2_SmbSsnData*, const uint16_t);
+static void DCE2_SmbInsertUid(DCE2_SmbSsnData*, const uint16_t);
+static void DCE2_SmbRemoveUid(DCE2_SmbSsnData*, const uint16_t);
+static void DCE2_SmbFileTrackerDataFree(void*);
+static void DCE2_SmbCleanSessionFileTracker(DCE2_SmbSsnData*, DCE2_SmbFileTracker*);
+static void DCE2_SmbRemoveFileTrackerFromRequestTrackers(DCE2_SmbSsnData*,
+ DCE2_SmbFileTracker*);
+static DCE2_SmbFileTracker* DCE2_SmbDequeueTmpFileTracker(DCE2_SmbSsnData*,
+ DCE2_SmbRequestTracker*, const uint16_t);
+static char* DCE2_SmbGetString(const uint8_t*, uint32_t, bool, bool);
+static inline DCE2_Ret DCE2_SmbProcessRequestData(DCE2_SmbSsnData*, const uint16_t,
+ const uint8_t*, uint32_t, uint64_t);
+static DCE2_Ret DCE2_SmbProcessResponseData(DCE2_SmbSsnData*,
+ const uint8_t*, uint32_t);
+static inline void DCE2_SmbCheckFmtData(DCE2_SmbSsnData*, const uint32_t,
+ const uint16_t, const uint8_t, const uint16_t, const uint16_t);
+static inline DCE2_Ret DCE2_SmbCheckData(DCE2_SmbSsnData*, const uint8_t*,
+ const uint8_t*, const uint32_t, const uint16_t, const uint32_t, uint16_t);
+static inline bool DCE2_SmbIsTransactionComplete(DCE2_SmbTransactionTracker*);
+static inline DCE2_Ret DCE2_SmbCheckAndXOffset(const uint8_t*,
+ const uint8_t*, const uint32_t);
+static void DCE2_SmbQueueTmpFileTracker(DCE2_SmbSsnData*,
+ DCE2_SmbRequestTracker*, const uint16_t, const uint16_t);
+static DCE2_Ret DCE2_SmbFindTid(DCE2_SmbSsnData*, const uint16_t);
+static void DCE2_SmbRemoveTid(DCE2_SmbSsnData*, const uint16_t);
+static void DCE2_SmbInsertTid(DCE2_SmbSsnData*, const uint16_t, const bool);
+static void DCE2_SmbInvalidShareCheck(DCE2_SmbSsnData*,
+ const SmbNtHdr*, const uint8_t*, uint32_t);
+static DCE2_Ret DCE2_SmbUpdateTransRequest(DCE2_SmbSsnData*, const SmbNtHdr*,
+ const DCE2_SmbComInfo*, const uint8_t*, uint32_t);
+static DCE2_Ret DCE2_SmbUpdateTransResponse(DCE2_SmbSsnData*, const SmbNtHdr*,
+ const DCE2_SmbComInfo*, const uint8_t*, uint32_t);
+static DCE2_Ret DCE2_SmbValidateTransactionFields(const uint8_t*,
+ const uint8_t*, const uint32_t, const uint16_t, const uint32_t,
+ const uint32_t, const uint32_t, const uint32_t, const uint32_t,
+ const uint32_t, const uint32_t, const uint32_t);
+static DCE2_Ret DCE2_SmbCheckTransDataParams(
+ const uint8_t*, const uint8_t*, const uint32_t, const uint16_t,
+ const uint32_t, const uint32_t, const uint32_t, const uint32_t);
+static DCE2_Ret DCE2_SmbCheckTotalCount(
+ const uint32_t, const uint32_t, const uint32_t);
+static DCE2_Ret DCE2_SmbValidateTransactionSent(
+ uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
+static DCE2_Ret DCE2_SmbOpen(DCE2_SmbSsnData*, const SmbNtHdr*,
+ const DCE2_SmbComInfo*, const uint8_t*, uint32_t);
+static DCE2_Ret DCE2_SmbCreate(DCE2_SmbSsnData*, const SmbNtHdr*,
+ const DCE2_SmbComInfo*, const uint8_t*, uint32_t);
+static DCE2_Ret DCE2_SmbClose(DCE2_SmbSsnData*, const SmbNtHdr*,
+ const DCE2_SmbComInfo*, const uint8_t*, uint32_t);
+static DCE2_Ret DCE2_SmbRename(DCE2_SmbSsnData*, const SmbNtHdr*,
+ const DCE2_SmbComInfo*, const uint8_t*, uint32_t);
+static DCE2_Ret DCE2_SmbRead(DCE2_SmbSsnData*, const SmbNtHdr*,
+ const DCE2_SmbComInfo*, const uint8_t*, uint32_t);
+static DCE2_Ret DCE2_SmbWrite(DCE2_SmbSsnData*, const SmbNtHdr*,
+ const DCE2_SmbComInfo*, const uint8_t*, uint32_t);
+static DCE2_Ret DCE2_SmbCreateNew(DCE2_SmbSsnData*, const SmbNtHdr*,
+ const DCE2_SmbComInfo*, const uint8_t*, uint32_t);
+static DCE2_Ret DCE2_SmbLockAndRead(DCE2_SmbSsnData*, const SmbNtHdr*,
+ const DCE2_SmbComInfo*, const uint8_t*, uint32_t);
+static DCE2_Ret DCE2_SmbWriteAndUnlock(DCE2_SmbSsnData*, const SmbNtHdr*,
+ const DCE2_SmbComInfo*, const uint8_t*, uint32_t);
+static DCE2_Ret DCE2_SmbOpenAndX(DCE2_SmbSsnData*, const SmbNtHdr*,
+ const DCE2_SmbComInfo*, const uint8_t*, uint32_t);
+static DCE2_Ret DCE2_SmbReadAndX(DCE2_SmbSsnData*, const SmbNtHdr*,
+ const DCE2_SmbComInfo*, const uint8_t*, uint32_t);
+static DCE2_Ret DCE2_SmbWriteAndX(DCE2_SmbSsnData*, const SmbNtHdr*,
+ const DCE2_SmbComInfo*, const uint8_t*, uint32_t);
+static DCE2_Ret DCE2_SmbWriteAndXRawRequest(DCE2_SmbSsnData*, const SmbNtHdr*,
+ const DCE2_SmbComInfo*, const uint8_t*, uint32_t);
+static DCE2_Ret DCE2_SmbSessionSetupAndX(DCE2_SmbSsnData*, const SmbNtHdr*,
+ const DCE2_SmbComInfo*, const uint8_t*, uint32_t);
+static DCE2_Ret DCE2_SmbNegotiate(DCE2_SmbSsnData*, const SmbNtHdr*,
+ const DCE2_SmbComInfo*, const uint8_t*, uint32_t);
+static DCE2_Ret DCE2_SmbTreeConnectAndX(DCE2_SmbSsnData*, const SmbNtHdr*,
+ const DCE2_SmbComInfo*, const uint8_t*, uint32_t);
+static DCE2_Ret DCE2_SmbTreeConnect(DCE2_SmbSsnData*, const SmbNtHdr*,
+ const DCE2_SmbComInfo*, const uint8_t*, uint32_t);
+static DCE2_Ret DCE2_SmbNtCreateAndX(DCE2_SmbSsnData*, const SmbNtHdr*,
+ const DCE2_SmbComInfo*, const uint8_t*, uint32_t);
+static DCE2_Ret DCE2_SmbTreeDisconnect(DCE2_SmbSsnData*, const SmbNtHdr*,
+ const DCE2_SmbComInfo*, const uint8_t*, uint32_t);
+static DCE2_Ret DCE2_SmbLogoffAndX(DCE2_SmbSsnData*, const SmbNtHdr*,
+ const DCE2_SmbComInfo*, const uint8_t*, uint32_t);
+static DCE2_Ret DCE2_SmbTransactionReq(DCE2_SmbSsnData*,
+ DCE2_SmbTransactionTracker*, const uint8_t*, uint32_t,
+ const uint8_t*, uint32_t);
+static DCE2_Ret DCE2_SmbTransaction(DCE2_SmbSsnData*, const SmbNtHdr*,
+ const DCE2_SmbComInfo*, const uint8_t*, uint32_t);
/********************************************************************
* Function: DCE2_SmbType()
static bool SmbStatusNtCodes(const SmbNtHdr* hdr)
{
- if (ntohs(hdr->smb_flg2) & SMB_FLG2__NT_CODES)
+ if (alignedNtohs(&hdr->smb_flg2) & SMB_FLG2__NT_CODES)
return true;
return false;
}
static inline uint32_t SmbNtStatus(const SmbNtHdr* hdr)
{
- return ntohl(hdr->smb_status.nt_status);
+ return alignedNtohl(&hdr->smb_status.nt_status);
}
static inline uint8_t SmbStatusClass(const SmbNtHdr* hdr)
static inline uint16_t SmbStatusCode(const SmbNtHdr* hdr)
{
- return ntohs(hdr->smb_status.smb_status.smb_code);
+ return alignedNtohs(&hdr->smb_status.smb_status.smb_code);
}
static inline uint8_t SmbNtStatusSeverity(const SmbNtHdr* hdr)
static inline uint16_t SmbPid(const SmbNtHdr* hdr)
{
- return ntohs(hdr->smb_pid);
+ return alignedNtohs(&hdr->smb_pid);
}
static inline uint16_t SmbMid(const SmbNtHdr* hdr)
{
- return ntohs(hdr->smb_mid);
+ return alignedNtohs(&hdr->smb_mid);
}
// This function is obviously deficient. Need to do a lot more
static inline uint16_t SmbUid(const SmbNtHdr* hdr)
{
- return ntohs(hdr->smb_uid);
+ return alignedNtohs(&hdr->smb_uid);
}
static inline uint16_t SmbTid(const SmbNtHdr* hdr)
{
- return ntohs(hdr->smb_tid);
+ return alignedNtohs(&hdr->smb_tid);
+}
+
+static inline bool SmbUnicode(const SmbNtHdr* hdr)
+{
+ return (alignedNtohs(&hdr->smb_flg2) & SMB_FLG2__UNICODE) ? true : false;
+}
+
+static inline bool SmbExtAttrReadOnly(const uint32_t ext_file_attrs)
+{
+ if (ext_file_attrs & SMB_EXT_FILE_ATTR_READONLY)
+ return true;
+ return false;
+}
+
+static inline bool SmbExtAttrHidden(const uint32_t ext_file_attrs)
+{
+ if (ext_file_attrs & SMB_EXT_FILE_ATTR_HIDDEN)
+ return true;
+ return false;
+}
+
+static inline bool SmbExtAttrSystem(const uint32_t ext_file_attrs)
+{
+ if (ext_file_attrs & SMB_EXT_FILE_ATTR_SYSTEM)
+ return true;
+ return false;
+}
+
+static inline bool SmbEvasiveFileAttrs(const uint32_t ext_file_attrs)
+{
+ return (SmbExtAttrReadOnly(ext_file_attrs)
+ && SmbExtAttrHidden(ext_file_attrs)
+ && SmbExtAttrSystem(ext_file_attrs));
+}
+
+static inline bool SmbErrorInvalidDeviceRequest(const SmbNtHdr* hdr)
+{
+ if (SmbStatusNtCodes(hdr))
+ {
+ if (SmbNtStatus(hdr) == SMB_NT_STATUS__INVALID_DEVICE_REQUEST)
+ return true;
+ }
+ else
+ {
+ if ((SmbStatusClass(hdr) == SMB_ERROR_CLASS__ERRSRV)
+ && (SmbStatusCode(hdr) == SMB_ERRSRV__INVALID_DEVICE))
+ return true;
+ }
+
+ return false;
+}
+
+static inline bool SmbErrorRangeNotLocked(const SmbNtHdr* hdr)
+{
+ if (SmbStatusNtCodes(hdr))
+ {
+ if (SmbNtStatus(hdr) == SMB_NT_STATUS__RANGE_NOT_LOCKED)
+ return true;
+ }
+ else
+ {
+ if ((SmbStatusClass(hdr) == SMB_ERROR_CLASS__ERRDOS)
+ && (SmbStatusCode(hdr) == SMB_ERRDOS__NOT_LOCKED))
+ return true;
+ }
+
+ return false;
+}
+
+/********************************************************************
+ * Function: DCE2_SmbCheckFmtData()
+ *
+ * Purpose:
+ * Checks the data count in commands with formats, e.g.
+ * SMB_COM_WRITE, SMB_COM_WRITE_AND_CLOSE, SMB_COM_WRITE_AND_UNLOCK.
+ *
+ * Arguments:
+ * DCE2_SmbSsnData * - SMB session data structure
+ * const uint32_t - remaining NetBIOS PDU length
+ * const uint16_t - advertised byte count
+ * const uint8_t - data format specifier
+ * const uint16_t - data count reported in command
+ * const uint16_t - data count reported in format field
+ *
+ * Returns: None
+ *
+ ********************************************************************/
+static inline void DCE2_SmbCheckFmtData(DCE2_SmbSsnData*,
+ const uint32_t nb_len, const uint16_t bcc, const uint8_t fmt,
+ const uint16_t com_dcnt, const uint16_t fmt_dcnt)
+{
+ if (fmt != SMB_FMT__DATA_BLOCK)
+ dce_alert(GID_DCE2, DCE2_SMB_BAD_FORM, (dce2CommonStats*)&dce2_smb_stats);
+ if (com_dcnt != fmt_dcnt)
+ dce_alert(GID_DCE2, DCE2_SMB_DCNT_MISMATCH, (dce2CommonStats*)&dce2_smb_stats);
+ if (com_dcnt != (bcc - 3))
+ dce_alert(GID_DCE2, DCE2_SMB_INVALID_DSIZE, (dce2CommonStats*)&dce2_smb_stats);
+ if (nb_len < com_dcnt)
+ dce_alert(GID_DCE2, DCE2_SMB_NB_LT_DSIZE, (dce2CommonStats*)&dce2_smb_stats);
}
/********************************************************************
return false;
}
-static void DCE2_SmbCleanTransactionTracker(DCE2_SmbTransactionTracker* ttracker)
+static DCE2_Ret DCE2_SmbInitFileTracker(DCE2_SmbSsnData* ssd,
+ DCE2_SmbFileTracker* ftracker, const bool is_ipc, const uint16_t uid,
+ const uint16_t tid, const int fid)
{
- Profile profile(dce2_smb_pstat_smb_req);
+ if (ftracker == nullptr)
+ return DCE2_RET__ERROR;
- if (ttracker == nullptr)
+ ftracker->uid = uid;
+ ftracker->tid = tid;
+ ftracker->fid = fid;
+ ftracker->is_ipc = is_ipc;
+ ftracker->file_name = nullptr;
+ if (is_ipc)
{
- return;
+ DCE2_CoTracker* co_tracker = (DCE2_CoTracker*)SnortAlloc(sizeof(DCE2_CoTracker));
+ if (co_tracker == nullptr)
+ return DCE2_RET__ERROR;
+ DCE2_CoInitTracker(co_tracker);
+ ftracker->fp_co_tracker = co_tracker;
+ ftracker->fp_byte_mode = false;
+ ftracker->fp_used = false;
+ ftracker->fp_writex_raw = nullptr;
+ }
+ else
+ {
+ ftracker->ff_file_size = 0;
+ ftracker->ff_file_offset = 0;
+ ftracker->ff_bytes_processed = 0;
+ ftracker->ff_file_direction = DCE2_SMB_FILE_DIRECTION__UNKNOWN;
+ ftracker->ff_file_chunks = nullptr;
+ ftracker->ff_bytes_queued = 0;
+ if ((ssd->fapi_ftracker == nullptr) && (ssd->max_file_depth != -1))
+ {
+ DebugFormat(DEBUG_DCE_SMB, "Designating file tracker "
+ "for file API processing: 0x%04X\n", (uint16_t)fid);
+ ssd->fapi_ftracker = ftracker;
+ }
}
- if (ttracker->dbuf != nullptr)
- DCE2_BufferDestroy(ttracker->dbuf);
+ return DCE2_RET__SUCCESS;
+}
- if (ttracker->pbuf != nullptr)
- DCE2_BufferDestroy(ttracker->pbuf);
+static DCE2_SmbFileTracker* DCE2_SmbNewFileTracker(DCE2_SmbSsnData* ssd,
+ const uint16_t uid, const uint16_t tid, const uint16_t fid)
+{
+ Profile profile(dce2_smb_pstat_smb_fid);
- memset(ttracker, 0, sizeof(*ttracker));
+ // Already have tracker for file API and not setting file data pointer
+ // so don't create new file tracker.
+ bool is_ipc = DCE2_SmbIsTidIPC(ssd, tid);
+ if (!is_ipc && (ssd->fapi_ftracker != nullptr)
+ && (DCE2_ScSmbFileDepth((dce2SmbProtoConf*)ssd->sd.config) == -1))
+ return nullptr;
+
+ DebugFormat(DEBUG_DCE_SMB, "Creating new file tracker "
+ "with Uid: %u, Tid: %u, Fid: 0x%04X\n", uid, tid, fid);
+
+ DCE2_SmbFileTracker* ftracker = nullptr;
+ if (ssd->ftracker.fid == DCE2_SENTINEL)
+ {
+ ftracker = &ssd->ftracker;
+ if (DCE2_SmbInitFileTracker(ssd, ftracker, is_ipc, uid, tid, (int)fid) !=
+ DCE2_RET__SUCCESS)
+ {
+ DCE2_SmbCleanFileTracker(ftracker);
+ return nullptr;
+ }
+ }
+ else
+ {
+ ftracker = (DCE2_SmbFileTracker*)SnortAlloc(sizeof(DCE2_SmbFileTracker));
+
+ if (ftracker == nullptr)
+ {
+ return nullptr;
+ }
+
+ if (DCE2_SmbInitFileTracker(ssd, ftracker, is_ipc, uid, tid, (int)fid) !=
+ DCE2_RET__SUCCESS)
+ {
+ DCE2_SmbCleanFileTracker(ftracker);
+ free((void*)ftracker);
+ return nullptr;
+ }
+
+ if (ssd->ftrackers == nullptr)
+ {
+ ssd->ftrackers = DCE2_ListNew(DCE2_LIST_TYPE__SPLAYED,
+ DCE2_SmbUidTidFidCompare, DCE2_SmbFileTrackerDataFree, nullptr,
+ DCE2_LIST_FLAG__NO_DUPS);
+
+ if (ssd->ftrackers == nullptr)
+ {
+ DCE2_SmbCleanSessionFileTracker(ssd, ftracker);
+ return nullptr;
+ }
+ }
+
+ if (DCE2_ListInsert(ssd->ftrackers, (void*)(uintptr_t)fid,
+ (void*)ftracker) != DCE2_RET__SUCCESS)
+ {
+ DCE2_SmbCleanSessionFileTracker(ssd, ftracker);
+ return nullptr;
+ }
+ }
+
+ return ftracker;
}
-static void DCE2_SmbCleanRequestTracker(DCE2_SmbRequestTracker* rtracker)
+static DCE2_SmbFileTracker* DCE2_SmbFindFileTracker(DCE2_SmbSsnData* ssd,
+ const uint16_t uid, const uint16_t tid, const uint16_t fid)
{
- Profile profile(dce2_smb_pstat_smb_req);
+ Profile profile(dce2_smb_pstat_smb_fid);
- if (rtracker == nullptr)
+ DebugFormat(DEBUG_DCE_SMB, "Finding file tracker with "
+ "Uid: %u, Tid: %u, Fid: 0x%04X ... ", uid, tid, fid);
+
+ DCE2_SmbFileTracker* ftracker;
+ if ((ssd->ftracker.fid != DCE2_SENTINEL) && (ssd->ftracker.fid == (int)fid))
{
- return;
+ ftracker = &ssd->ftracker;
+ }
+ else
+ {
+ ftracker = (DCE2_SmbFileTracker*)
+ DCE2_ListFind(ssd->ftrackers, (void*)(uintptr_t)fid);
}
- if (rtracker->mid == DCE2_SENTINEL)
+ if (ftracker == nullptr)
{
- return;
+ DebugMessage(DEBUG_DCE_SMB, "Not found.\n");
+ return nullptr;
}
- rtracker->mid = DCE2_SENTINEL;
- rtracker->ftracker = nullptr;
- rtracker->sequential_only = false;
+ // Note IPC Tid has already been validated in initial processing
+ const DCE2_Policy policy = DCE2_SsnGetServerPolicy(&ssd->sd);
+ switch (policy)
+ {
+ case DCE2_POLICY__SAMBA:
+ case DCE2_POLICY__SAMBA_3_0_37:
+ // Only Uid used to open file can be used to make a request
+ if (ftracker->uid != uid)
+ {
+ DebugMessage(DEBUG_DCE_SMB, "Not found.\n");
+ return nullptr;
+ }
- DCE2_SmbCleanTransactionTracker(&rtracker->ttracker);
+ break;
- DCE2_QueueDestroy(rtracker->ft_queue);
- rtracker->ft_queue = nullptr;
+ case DCE2_POLICY__WIN2000:
+ case DCE2_POLICY__SAMBA_3_0_20:
+ case DCE2_POLICY__SAMBA_3_0_22:
+ // Any valid Uid can be used to make a request to a file ...
+ // except for Windows 2000 on the first use.
+ if ((policy != DCE2_POLICY__WIN2000) || (ftracker->is_ipc && ftracker->fp_used))
+ {
+ // Check that the Uid exists
+ if (DCE2_SmbFindUid(ssd, uid) != DCE2_RET__SUCCESS)
+ {
+ DebugMessage(DEBUG_DCE_SMB, "Not found.\n");
+ return nullptr;
+ }
- if (rtracker->file_name != nullptr)
- {
- free((void*)rtracker->file_name);
- rtracker->file_name = nullptr;
+ break;
+ }
+
+ // Fall through for Windows 2000 for first request to file
+
+ case DCE2_POLICY__WIN2003:
+ case DCE2_POLICY__WINXP:
+ case DCE2_POLICY__WINVISTA:
+ case DCE2_POLICY__WIN2008:
+ case DCE2_POLICY__WIN7:
+ // Both Uid and Tid used to create file must be used to make a request
+ if ((ftracker->uid != uid) || (ftracker->tid != tid))
+ {
+ DebugMessage(DEBUG_DCE_SMB, "Not found.\n");
+ return nullptr;
+ }
+
+ break;
+
+ default:
+ DebugFormat(DEBUG_DCE_SMB, "%s(%d) Invalid policy: %d",
+ __FILE__, __LINE__, policy);
+ break;
}
+
+ DebugFormat(DEBUG_DCE_SMB, "Found with "
+ "Uid: %u, Tid: %u, Fid: 0x%04X\n",
+ ftracker->uid, ftracker->tid, ftracker->fid);
+ return ftracker;
}
-static void DCE2_SmbRequestTrackerDataFree(void* data)
+static DCE2_SmbFileTracker* DCE2_SmbGetFileTracker(DCE2_SmbSsnData* ssd,
+ const uint16_t fid)
{
- DCE2_SmbRequestTracker* rtracker = (DCE2_SmbRequestTracker*)data;
+ DCE2_SmbFileTracker* ftracker = ssd->cur_rtracker->ftracker;
- if (rtracker == nullptr)
- return;
+ if (ftracker == nullptr)
+ {
+ // Write could've been chained to an OpenAndX or NtCreateAndX so a
+ // temporary file tracker would've been created until we get the
+ // response with the Fid returned from the OpenAndX / NtCreateAndX
+ ftracker = DCE2_SmbGetTmpFileTracker(ssd->cur_rtracker);
+ if (ftracker == nullptr)
+ {
+ // Otherwise find it with the passed in Fid
+ ftracker = DCE2_SmbFindFileTracker(ssd, ssd->cur_rtracker->uid,
+ ssd->cur_rtracker->tid, fid);
+ }
+ }
- DebugFormat(DEBUG_DCE_SMB, "Freeing request tracker: "
- "Uid: %u, Tid: %u, Pid: %u, Mid: %u\n",
- rtracker->uid, rtracker->tid, rtracker->pid, rtracker->mid);
+ return ftracker;
+}
- DCE2_SmbCleanRequestTracker(rtracker);
- free((void*)rtracker);
+static DCE2_SmbFileTracker* DCE2_SmbGetTmpFileTracker(DCE2_SmbRequestTracker* rtracker)
+{
+ if (!DCE2_QueueIsEmpty(rtracker->ft_queue))
+ return (DCE2_SmbFileTracker*)DCE2_QueueLast(rtracker->ft_queue);
+ return nullptr;
}
-static DCE2_SmbRequestTracker* DCE2_SmbFindRequestTracker(DCE2_SmbSsnData* ssd,
- const SmbNtHdr* smb_hdr)
+static void DCE2_SmbRemoveFileTracker(DCE2_SmbSsnData* ssd, DCE2_SmbFileTracker* ftracker)
{
- uint16_t uid = SmbUid(smb_hdr);
- uint16_t tid = SmbTid(smb_hdr);
- uint16_t pid = SmbPid(smb_hdr);
- uint16_t mid = SmbMid(smb_hdr);
+ if (ftracker == nullptr)
+ return;
- Profile profile(dce2_smb_pstat_smb_req);
+ Profile profile(dce2_smb_pstat_smb_fid);
- DebugFormat(DEBUG_DCE_SMB, "Find request tracker => "
- "Uid: %u, Tid: %u, Pid: %u, Mid: %u ... ", uid, tid, pid, mid);
+ DebugFormat(DEBUG_DCE_SMB,
+ "Removing file tracker with Fid: 0x%04X\n", ftracker->fid);
- DCE2_SmbRequestTracker* tmp_rtracker = &ssd->rtracker;
- int smb_com = SmbCom(smb_hdr);
- switch (smb_com)
+ // FIXIT-M uncomment when file api related code is ported
+ /*
+ if (ssd->fapi_ftracker == ftracker)
+ DCE2_SmbFinishFileAPI(ssd);
+ */
+
+ //FIXIT-M port active response related code
+/*
+#ifdef ACTIVE_RESPONSE
+ if (ssd->fb_ftracker == ftracker)
+ DCE2_SmbFinishFileBlockVerdict(ssd);
+#endif
+*/
+ if (ftracker == &ssd->ftracker)
+ DCE2_SmbCleanFileTracker(&ssd->ftracker);
+ else if (ssd->ftrackers != nullptr)
+ DCE2_ListRemove(ssd->ftrackers, (void*)(uintptr_t)ftracker->fid);
+
+ DCE2_SmbRemoveFileTrackerFromRequestTrackers(ssd, ftracker);
+}
+
+static void DCE2_SmbCleanFileTracker(DCE2_SmbFileTracker* ftracker)
+{
+ if (ftracker == nullptr)
+ return;
+
+ Profile profile(dce2_smb_pstat_smb_fid);
+
+ ftracker->fid = DCE2_SENTINEL;
+ if (ftracker->file_name != nullptr)
{
- case SMB_COM_TRANSACTION_SECONDARY:
- smb_com = SMB_COM_TRANSACTION;
- break;
- case SMB_COM_TRANSACTION2_SECONDARY:
- smb_com = SMB_COM_TRANSACTION2;
- break;
- case SMB_COM_NT_TRANSACT_SECONDARY:
- smb_com = SMB_COM_NT_TRANSACT;
- break;
- case SMB_COM_WRITE_COMPLETE:
- smb_com = SMB_COM_WRITE_RAW;
- break;
- default:
- break;
+ free((void*)ftracker->file_name);
+ ftracker->file_name = nullptr;
}
- DCE2_SmbRequestTracker* first_rtracker = nullptr;
- DCE2_SmbRequestTracker* win_rtracker = nullptr;
- DCE2_SmbRequestTracker* first_mid_rtracker = nullptr;
- DCE2_SmbRequestTracker* ret_rtracker = nullptr;
- while (tmp_rtracker != nullptr)
+ if (ftracker->is_ipc)
{
- if ((tmp_rtracker->mid == (int)mid) && (tmp_rtracker->smb_com == smb_com))
+ ftracker->fp_used = 0;
+ ftracker->fp_byte_mode = 0;
+
+ if (ftracker->fp_writex_raw != nullptr)
{
- // This is the normal case except for SessionSetupAndX and
- // TreeConnect/TreeConnectAndX which will fall into the
- // default case below.
- if ((tmp_rtracker->pid == pid) && (tmp_rtracker->uid == uid)
- && (tmp_rtracker->tid == tid))
- {
- ret_rtracker = tmp_rtracker;
- }
- else
- {
- switch (smb_com)
- {
- case SMB_COM_TRANSACTION:
- case SMB_COM_TRANSACTION2:
- case SMB_COM_NT_TRANSACT:
- case SMB_COM_TRANSACTION_SECONDARY:
- case SMB_COM_TRANSACTION2_SECONDARY:
- case SMB_COM_NT_TRANSACT_SECONDARY:
- // These should conform to above
- break;
- default:
- if (tmp_rtracker->pid == pid)
- ret_rtracker = tmp_rtracker;
- break;
- }
- }
+ DCE2_BufferDestroy(ftracker->fp_writex_raw->buf);
+ free((void*)ftracker->fp_writex_raw);
+ ftracker->fp_writex_raw = nullptr;
+ }
- if (ret_rtracker != nullptr)
- {
- DebugMessage(DEBUG_DCE_SMB, "Found.\n");
- return ret_rtracker;
- }
+ if (ftracker->fp_co_tracker != nullptr)
+ {
+ DCE2_CoCleanTracker(ftracker->fp_co_tracker);
+ free((void*)ftracker->fp_co_tracker);
+ ftracker->fp_co_tracker = nullptr;
+ }
+ }
+ else
+ {
+ ftracker->ff_file_size = 0;
+ ftracker->ff_file_offset = 0;
+ ftracker->ff_bytes_processed = 0;
+ ftracker->ff_file_direction = DCE2_SMB_FILE_DIRECTION__UNKNOWN;
+ ftracker->ff_bytes_queued = 0;
+ ftracker->ff_sequential_only = false;
+ if (ftracker->ff_file_chunks != nullptr)
+ {
+ DCE2_ListDestroy(ftracker->ff_file_chunks);
+ ftracker->ff_file_chunks = nullptr;
+ }
+ }
+}
- // Take the first one where the PIDs also match
- // in the case of the Transacts above
- if ((tmp_rtracker->pid == pid) && (win_rtracker == nullptr))
- win_rtracker = tmp_rtracker;
+static int DCE2_SmbUidTidFidCompare(const void* a, const void* b)
+{
+ int x = (int)(uintptr_t)a;
+ int y = (int)(uintptr_t)b;
- // Set this to the first matching request in the queue
- // where the Mid matches. Don't set for Windows if from
- // client since PID/MID are necessary
- if (((DCE2_SmbType(ssd) == SMB_TYPE__RESPONSE)
- || !DCE2_SsnIsWindowsPolicy(&ssd->sd))
- && first_mid_rtracker == nullptr)
+ if (x == y)
+ return 0;
+
+ /* Only care about equality for finding */
+ return -1;
+}
+
+static DCE2_Ret DCE2_SmbFindUid(DCE2_SmbSsnData* ssd, const uint16_t uid)
+{
+ DCE2_Ret status;
+
+ Profile profile(dce2_smb_pstat_smb_uid);
+
+ if ((ssd->uid != DCE2_SENTINEL) && (ssd->uid == (int)uid))
+ status = DCE2_RET__SUCCESS;
+ else
+ status = DCE2_ListFindKey(ssd->uids, (void*)(uintptr_t)uid);
+
+ return status;
+}
+
+static void DCE2_SmbInsertUid(DCE2_SmbSsnData* ssd, const uint16_t uid)
+{
+ Profile profile(dce2_smb_pstat_smb_uid);
+
+ DebugFormat(DEBUG_DCE_SMB, "Inserting Uid: %u\n", uid);
+
+ if (ssd->uid == DCE2_SENTINEL)
+ {
+ ssd->uid = (int)uid;
+ }
+ else
+ {
+ if (ssd->uids == nullptr)
+ {
+ ssd->uids = DCE2_ListNew(DCE2_LIST_TYPE__SPLAYED, DCE2_SmbUidTidFidCompare,
+ nullptr, nullptr, DCE2_LIST_FLAG__NO_DUPS);
+
+ if (ssd->uids == nullptr)
{
- first_mid_rtracker = tmp_rtracker;
+ return;
}
}
- // Set the first one we see for early Samba versions
- if ((first_rtracker == nullptr) && (tmp_rtracker->mid != DCE2_SENTINEL)
- && (tmp_rtracker->smb_com == smb_com))
- first_rtracker = tmp_rtracker;
-
- // Look at the next request in the queue
- if (tmp_rtracker == &ssd->rtracker)
- tmp_rtracker = (DCE2_SmbRequestTracker*)DCE2_QueueFirst(ssd->rtrackers);
- else
- tmp_rtracker = (DCE2_SmbRequestTracker*)DCE2_QueueNext(ssd->rtrackers);
+ DCE2_ListInsert(ssd->uids, (void*)(uintptr_t)uid, (void*)(uintptr_t)uid);
}
+}
+
+static void DCE2_SmbRemoveUid(DCE2_SmbSsnData* ssd, const uint16_t uid)
+{
+ const DCE2_Policy policy = DCE2_SsnGetServerPolicy(&ssd->sd);
+
+ Profile profile(dce2_smb_pstat_smb_uid);
+
+ DebugFormat(DEBUG_DCE_SMB,"Removing Uid: %u\n", uid);
+
+ if ((ssd->uid != DCE2_SENTINEL) && (ssd->uid == (int)uid))
+ ssd->uid = DCE2_SENTINEL;
+ else
+ DCE2_ListRemove(ssd->uids, (void*)(uintptr_t)uid);
- DCE2_Policy policy = DCE2_SsnGetPolicy(&ssd->sd);
switch (policy)
{
- case DCE2_POLICY__SAMBA_3_0_20:
- case DCE2_POLICY__SAMBA_3_0_22:
- ret_rtracker = first_rtracker;
- break;
- case DCE2_POLICY__SAMBA:
- case DCE2_POLICY__SAMBA_3_0_37:
- ret_rtracker = first_mid_rtracker;
- break;
case DCE2_POLICY__WIN2000:
+ case DCE2_POLICY__WIN2003:
case DCE2_POLICY__WINXP:
case DCE2_POLICY__WINVISTA:
- case DCE2_POLICY__WIN2003:
case DCE2_POLICY__WIN2008:
case DCE2_POLICY__WIN7:
- if (win_rtracker != nullptr)
- ret_rtracker = win_rtracker;
- else
- ret_rtracker = first_mid_rtracker;
+ case DCE2_POLICY__SAMBA:
+ case DCE2_POLICY__SAMBA_3_0_37:
+ // Removing uid invalidates any fid that was created with it */
+ if ((ssd->ftracker.fid != DCE2_SENTINEL) &&
+ (ssd->ftracker.uid == uid))
+ {
+ DCE2_SmbRemoveFileTracker(ssd, &ssd->ftracker);
+ }
+
+ if (ssd->ftrackers != nullptr)
+ {
+ DCE2_SmbFileTracker* ftracker;
+
+ for (ftracker = (DCE2_SmbFileTracker*)DCE2_ListFirst(ssd->ftrackers);
+ ftracker != nullptr;
+ ftracker = (DCE2_SmbFileTracker*)DCE2_ListNext(ssd->ftrackers))
+ {
+ if (ftracker->uid == uid)
+ {
+// FIXIT-M uncomment after file api is ported
+/*
+ if (ssd->fapi_ftracker == ftracker)
+ DCE2_SmbFinishFileAPI(ssd);
+
+#ifdef ACTIVE_RESPONSE
+ if (ssd->fb_ftracker == ftracker)
+ DCE2_SmbFinishFileBlockVerdict(ssd);
+#endif
+*/
+ DCE2_ListRemoveCurrent(ssd->ftrackers);
+ DCE2_SmbRemoveFileTrackerFromRequestTrackers(ssd, ftracker);
+ }
+ }
+ }
+
+ break;
+
+ case DCE2_POLICY__SAMBA_3_0_20:
+ case DCE2_POLICY__SAMBA_3_0_22:
+ // Removing Uid used to create file doesn't invalidate it.
break;
+
default:
DebugFormat(DEBUG_DCE_SMB, "%s(%d) Invalid policy: %d",
__FILE__, __LINE__, policy);
break;
}
+}
- return ret_rtracker;
+static void DCE2_SmbFileTrackerDataFree(void* data)
+{
+ DCE2_SmbFileTracker* ftracker = (DCE2_SmbFileTracker*)data;
+
+ if (ftracker == nullptr)
+ return;
+
+ DebugFormat(DEBUG_DCE_SMB, "Freeing file tracker: "
+ "Uid: %u, Tid: %u, Fid: 0x%04X\n",
+ ftracker->uid, ftracker->tid, ftracker->fid);
+
+ DCE2_SmbCleanFileTracker(ftracker);
+ free((void*)ftracker);
}
-static DCE2_SmbRequestTracker* DCE2_SmbNewRequestTracker(DCE2_SmbSsnData* ssd,
- const SmbNtHdr* smb_hdr)
+/********************************************************************
+ *
+ * Remove file tracker and associated pointers in session
+ *
+ ********************************************************************/
+static void DCE2_SmbCleanSessionFileTracker(DCE2_SmbSsnData* ssd, DCE2_SmbFileTracker* ftracker)
{
- uint16_t pid = SmbPid(smb_hdr);
- uint16_t mid = SmbMid(smb_hdr);
- uint16_t uid = SmbUid(smb_hdr);
- uint16_t tid = SmbTid(smb_hdr);
+ DCE2_SmbCleanFileTracker(ftracker);
+ free((void*)ftracker);
+ if (ssd->fapi_ftracker == ftracker)
+ ssd->fapi_ftracker = nullptr;
+}
+static void DCE2_SmbCleanTransactionTracker(DCE2_SmbTransactionTracker* ttracker)
+{
Profile profile(dce2_smb_pstat_smb_req);
- if (ssd == nullptr)
+ if (ttracker == nullptr)
{
- return nullptr;
+ return;
}
- if (ssd->outstanding_requests >= ssd->max_outstanding_requests)
+ if (ttracker->dbuf != nullptr)
+ DCE2_BufferDestroy(ttracker->dbuf);
+
+ if (ttracker->pbuf != nullptr)
+ DCE2_BufferDestroy(ttracker->pbuf);
+
+ memset(ttracker, 0, sizeof(*ttracker));
+}
+
+static void DCE2_SmbCleanRequestTracker(DCE2_SmbRequestTracker* rtracker)
+{
+ Profile profile(dce2_smb_pstat_smb_req);
+
+ if (rtracker == nullptr)
{
- dce_alert(GID_DCE2, DCE2_SMB_MAX_REQS_EXCEEDED, (dce2CommonStats*)&dce2_smb_stats);
+ return;
}
- // Check for outstanding requests with the same MID
- DCE2_SmbRequestTracker* tmp_rtracker = &ssd->rtracker;
- while ((tmp_rtracker != nullptr) && (tmp_rtracker->mid != DCE2_SENTINEL))
+ if (rtracker->mid == DCE2_SENTINEL)
{
- if (tmp_rtracker->mid == (int)mid)
- {
- // Have yet to see an MID repeatedly used so shouldn't
- // be any outstanding requests with the same MID.
- dce_alert(GID_DCE2, DCE2_SMB_REQS_SAME_MID, (dce2CommonStats*)&dce2_smb_stats);
- break;
- }
+ return;
+ }
- // Look at the next request in the queue
- if (tmp_rtracker == &ssd->rtracker)
- tmp_rtracker = (DCE2_SmbRequestTracker*)DCE2_QueueFirst(ssd->rtrackers);
- else
- tmp_rtracker = (DCE2_SmbRequestTracker*)DCE2_QueueNext(ssd->rtrackers);
+ rtracker->mid = DCE2_SENTINEL;
+ rtracker->ftracker = nullptr;
+ rtracker->sequential_only = false;
+
+ DCE2_SmbCleanTransactionTracker(&rtracker->ttracker);
+
+ DCE2_QueueDestroy(rtracker->ft_queue);
+ rtracker->ft_queue = nullptr;
+
+ if (rtracker->file_name != nullptr)
+ {
+ free((void*)rtracker->file_name);
+ rtracker->file_name = nullptr;
}
+}
- DCE2_SmbRequestTracker* rtracker = nullptr;
- if (ssd->rtracker.mid == DCE2_SENTINEL)
+static inline void DCE2_SmbRemoveRequestTracker(DCE2_SmbSsnData* ssd,
+ DCE2_SmbRequestTracker* rtracker)
+{
+ Profile profile(dce2_smb_pstat_smb_req);
+
+ if ((ssd == nullptr) || (rtracker == nullptr))
{
- rtracker = &ssd->rtracker;
+ return;
}
- else
+
+ DebugFormat(DEBUG_DCE_SMB, "Removing request tracker => "
+ "Uid: %u, Tid: %u, Pid: %u, Mid: %u ... ",
+ rtracker->uid, rtracker->tid, rtracker->pid, rtracker->mid);
+
+ if (rtracker == &ssd->rtracker)
{
- if (ssd->rtrackers == nullptr)
+ DebugMessage(DEBUG_DCE_SMB, "Removed\n");
+
+ DCE2_SmbCleanRequestTracker(&ssd->rtracker);
+ ssd->outstanding_requests--;
+ return;
+ }
+
+ DCE2_SmbRequestTracker* tmp_node;
+ for (tmp_node = (DCE2_SmbRequestTracker*)DCE2_QueueFirst(ssd->rtrackers);
+ tmp_node != nullptr;
+ tmp_node = (DCE2_SmbRequestTracker*)DCE2_QueueNext(ssd->rtrackers))
+ {
+ if (tmp_node == (void*)rtracker)
{
- ssd->rtrackers = DCE2_QueueNew(DCE2_SmbRequestTrackerDataFree);
- if (ssd->rtrackers == nullptr)
- {
- return nullptr;
- }
+ DebugMessage(DEBUG_DCE_SMB, "Removed\n");
+
+ DCE2_QueueRemoveCurrent(ssd->rtrackers);
+ ssd->outstanding_requests--;
+ return;
}
+ }
- rtracker = (DCE2_SmbRequestTracker*)SnortAlloc(sizeof(DCE2_SmbRequestTracker));
- if (rtracker == nullptr)
+ DebugMessage(DEBUG_DCE_SMB, "Not removed.\n");
+}
+
+static void DCE2_SmbRemoveFileTrackerFromRequestTrackers(DCE2_SmbSsnData* ssd,
+ DCE2_SmbFileTracker* ftracker)
+{
+ if (ftracker == nullptr)
+ return;
+
+ // NULL out file trackers of any outstanding requests
+ // that reference this file tracker
+ if (ssd->rtracker.ftracker == ftracker)
+ ssd->rtracker.ftracker = nullptr;
+
+ if ((ssd->cur_rtracker != nullptr) && (ssd->cur_rtracker->ftracker == ftracker))
+ ssd->cur_rtracker->ftracker = nullptr;
+
+ DCE2_SmbRequestTracker* rtracker;
+ for (rtracker = (DCE2_SmbRequestTracker*)DCE2_QueueFirst(ssd->rtrackers);
+ rtracker != nullptr;
+ rtracker = (DCE2_SmbRequestTracker*)DCE2_QueueNext(ssd->rtrackers))
+ {
+ if (rtracker->ftracker == ftracker)
+ rtracker->ftracker = nullptr;
+ }
+}
+
+static DCE2_SmbFileTracker* DCE2_SmbDequeueTmpFileTracker(DCE2_SmbSsnData* ssd,
+ DCE2_SmbRequestTracker* rtracker, const uint16_t fid)
+{
+ Profile profile(dce2_smb_pstat_smb_fid);
+
+ DebugFormat(DEBUG_DCE_SMB, "Dequeueing file tracker "
+ "and binding to fid: 0x%04X\n", fid);
+
+ DCE2_SmbFileTracker* ftracker = (DCE2_SmbFileTracker*)DCE2_QueueDequeue(rtracker->ft_queue);
+ if (ftracker == nullptr)
+ {
+ return nullptr;
+ }
+
+ if (ssd->ftracker.fid == DCE2_SENTINEL)
+ {
+ memcpy(&ssd->ftracker, ftracker, sizeof(DCE2_SmbFileTracker));
+ free((void*)ftracker);
+ if (ssd->fapi_ftracker == ftracker)
+ ssd->fapi_ftracker = &ssd->ftracker;
+ ftracker = &ssd->ftracker;
+ }
+ else
+ {
+ if (ssd->ftrackers == nullptr)
{
- return nullptr;
+ ssd->ftrackers = DCE2_ListNew(DCE2_LIST_TYPE__SPLAYED,
+ DCE2_SmbUidTidFidCompare, DCE2_SmbFileTrackerDataFree, nullptr,
+ DCE2_LIST_FLAG__NO_DUPS);
+
+ if (ssd->ftrackers == nullptr)
+ {
+ DCE2_SmbCleanSessionFileTracker(ssd, ftracker);
+ return nullptr;
+ }
}
- if (DCE2_QueueEnqueue(ssd->rtrackers, (void*)rtracker) != DCE2_RET__SUCCESS)
+ if (DCE2_ListInsert(ssd->ftrackers, (void*)(uintptr_t)fid,
+ (void*)ftracker) != DCE2_RET__SUCCESS)
{
- free((void*)rtracker);
+ DCE2_SmbCleanSessionFileTracker(ssd, ftracker);
return nullptr;
}
}
- rtracker->smb_com = SmbCom(smb_hdr);
- rtracker->uid = uid;
- rtracker->tid = tid;
- rtracker->pid = pid;
- rtracker->mid = (int)mid;
- memset(&rtracker->ttracker, 0, sizeof(rtracker->ttracker));
- rtracker->ftracker = nullptr;
- rtracker->sequential_only = false;
+ // Other values were intialized when queueing.
+ ftracker->fid = (int)fid;
- ssd->outstanding_requests++;
- if (ssd->outstanding_requests > dce2_smb_stats.smb_max_outstanding_requests)
- dce2_smb_stats.smb_max_outstanding_requests = ssd->outstanding_requests;
+ return ftracker;
+}
- DebugFormat(DEBUG_DCE_SMB, "Added new request tracker => "
+static void DCE2_SmbRequestTrackerDataFree(void* data)
+{
+ DCE2_SmbRequestTracker* rtracker = (DCE2_SmbRequestTracker*)data;
+
+ if (rtracker == nullptr)
+ return;
+
+ DebugFormat(DEBUG_DCE_SMB, "Freeing request tracker: "
"Uid: %u, Tid: %u, Pid: %u, Mid: %u\n",
rtracker->uid, rtracker->tid, rtracker->pid, rtracker->mid);
- DebugFormat(DEBUG_DCE_SMB,
- "Current outstanding requests: %u\n", ssd->outstanding_requests);
- return rtracker;
+ DCE2_SmbCleanRequestTracker(rtracker);
+ free((void*)rtracker);
}
-/********************************************************************
- * Function: DCE2_SmbHdrChecks()
- *
- * Checks some relevant fields in the header to make sure they're
- * sane.
- * Side effects are potential alerts for anomolous behavior.
- *
- * Arguments:
- * DCE2_SmbSsnData *
- * Pointer to the session data structure.
- * SmbNtHdr *
- * Pointer to the header struct layed over the packet data.
- *
- * Returns:
- * DCE2_Ret
- * DCE2_RET__IGNORE if we should continue processing, but
- * ignore data because of the error.
- * DCE2_RET__SUCCESS if we should continue processing.
- *
- ********************************************************************/
-static DCE2_Ret DCE2_SmbHdrChecks(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr)
+static DCE2_Ret DCE2_SmbFindTid(DCE2_SmbSsnData* ssd, const uint16_t tid)
{
- Packet* p = ssd->sd.wire_pkt;
+ DCE2_Ret status;
- if ((DCE2_SsnFromServer(p) && (SmbType(smb_hdr) == SMB_TYPE__REQUEST)) ||
- (DCE2_SsnFromClient(p) && (SmbType(smb_hdr) == SMB_TYPE__RESPONSE)))
- {
- // FIXIT-M port segment check
- // Same for all cases below
- dce_alert(GID_DCE2, DCE2_SMB_BAD_TYPE, (dce2CommonStats*)&dce2_smb_stats);
- // Continue looking at traffic. Neither Windows nor Samba seem
- // to care, or even look at this flag
- }
+ Profile profile(dce2_smb_pstat_smb_tid);
- if ((SmbId(smb_hdr) != DCE2_SMB_ID)
- && (SmbId(smb_hdr) != DCE2_SMB2_ID))
- {
- dce_alert(GID_DCE2, DCE2_SMB_BAD_ID, (dce2CommonStats*)&dce2_smb_stats);
- return DCE2_RET__IGNORE;
- }
+ if ((ssd->tid != DCE2_SENTINEL) && ((ssd->tid & 0x0000ffff) == (int)tid))
+ status = DCE2_RET__SUCCESS;
+ else
+ status = DCE2_ListFindKey(ssd->tids, (void*)(uintptr_t)tid);
- return DCE2_RET__SUCCESS;
+ return status;
}
-/********************************************************************
- * Function: DCE2_SmbGetMinByteCount()
- *
- * Purpose:
- * Returns the minimum byte count for the given command request
- * or response.
- *
- * Arguments:
- * uint8_t - the SMB command code
- * uint8_t - SMB_TYPE__REQUEST or SMB_TYPE__RESPONSE
- *
- * Returns:
- * uint16_t - the minimum byte count
- *
- ********************************************************************/
-static inline uint16_t DCE2_SmbGetMinByteCount(uint8_t com, uint8_t resp)
+static void DCE2_SmbRemoveTid(DCE2_SmbSsnData* ssd, const uint16_t tid)
{
- return smb_bccs[com][resp][0];
-}
+ Profile profile(dce2_smb_pstat_smb_tid);
-/********************************************************************
- * Function: DCE2_SmbCheckCommand()
- *
- * Purpose:
- * Checks basic validity of an SMB command.
- *
- * Arguments:
- * DCE2_SmbSsnData * - pointer to session data structure
- * SmbNtHdr * - pointer to the SMB header structure
- * uint8_t - the SMB command code, i.e. SMB_COM_*
- * uint8_t * - current pointer to data, i.e. the command
- * uint32_t - the remaining length
- * DCE2_SmbComInfo & -
- * Populated structure for command processing
- *
- * Returns: None
- *
- ********************************************************************/
-static void DCE2_SmbCheckCommand(DCE2_SmbSsnData* ssd,
- const SmbNtHdr* smb_hdr, const uint8_t smb_com,
- const uint8_t* nb_ptr, uint32_t nb_len, DCE2_SmbComInfo& com_info)
-{
- // Check for server error response
- if (com_info.smb_type == SMB_TYPE__RESPONSE)
+ DebugFormat(DEBUG_DCE_SMB, "Removing Tid: %u\n", tid);
+
+ if ((ssd->tid != DCE2_SENTINEL) && ((ssd->tid & 0x0000ffff) == (int)tid))
+ ssd->tid = DCE2_SENTINEL;
+ else
+ DCE2_ListRemove(ssd->tids, (void*)(uintptr_t)tid);
+
+ // Removing Tid invalidates files created with it
+ if ((ssd->ftracker.fid != DCE2_SENTINEL)
+ && (ssd->ftracker.tid == tid))
{
- const SmbEmptyCom* ec = (SmbEmptyCom*)nb_ptr;
+ DCE2_SmbRemoveFileTracker(ssd, &ssd->ftracker);
+ }
- // Verify there is enough data to do checks
- if (nb_len < sizeof(SmbEmptyCom))
- {
- dce_alert(GID_DCE2, DCE2_SMB_NB_LT_COM, (dce2CommonStats*)&dce2_smb_stats);
- com_info.cmd_error |= DCE2_SMB_COM_ERROR__BAD_LENGTH;
- return;
- }
+ if (ssd->ftrackers != nullptr)
+ {
+ DCE2_SmbFileTracker* ftracker;
- // If word and byte counts are zero and there is an error
- // the server didn't accept client request
- if ((SmbEmptyComWct(ec) == 0)
- && (SmbEmptyComBcc(ec) == 0) && SmbError(smb_hdr))
+ for (ftracker = (DCE2_SmbFileTracker*)DCE2_ListFirst(ssd->ftrackers);
+ ftracker != nullptr;
+ ftracker = (DCE2_SmbFileTracker*)DCE2_ListNext(ssd->ftrackers))
{
- DebugFormat(DEBUG_DCE_SMB,
- "Response error: 0x%08X\n", SmbNtStatus(smb_hdr));
-
- // If broken pipe, clean up data associated with open named pipe
- if (SmbBrokenPipe(smb_hdr))
+ if (ftracker->tid == (int)tid)
{
- DebugMessage(DEBUG_DCE_SMB,"Broken or disconnected pipe.\n");
- // FIXIT-M uncomment once file tracking is ported
- // DCE2_SmbRemoveFileTracker(ssd, ssd->cur_rtracker->ftracker);
- }
+// FIXIT-M uncomment once file api is ported
+/*
+ if (ssd->fapi_ftracker == ftracker)
+ DCE2_SmbFinishFileAPI(ssd);
- com_info.cmd_error |= DCE2_SMB_COM_ERROR__STATUS_ERROR;
- return;
+#ifdef ACTIVE_RESPONSE
+ if (ssd->fb_ftracker == ftracker)
+ DCE2_SmbFinishFileBlockVerdict(ssd);
+#endif
+*/
+ DCE2_ListRemoveCurrent(ssd->ftrackers);
+ DCE2_SmbRemoveFileTrackerFromRequestTrackers(ssd, ftracker);
+ }
}
}
+}
- // Set the header size to the minimum size the command can be
- // without the byte count to make sure there is enough data to
- // get the word count.
- SmbAndXCom andx_com = smb_chain_map[smb_com];
- int chk_com_size;
- if (andx_com == SMB_ANDX_COM__NONE)
- chk_com_size = sizeof(SmbCommon);
- else
- chk_com_size = sizeof(SmbAndXCommon);
+static void DCE2_SmbInsertTid(DCE2_SmbSsnData* ssd,
+ const uint16_t tid, const bool is_ipc)
+{
+ Profile profile(dce2_smb_pstat_smb_tid);
- // Verify there is enough data to do checks
- if (nb_len < (uint32_t)chk_com_size)
+ if (!is_ipc && (!DCE2_ScSmbFileInspection((dce2SmbProtoConf*)ssd->sd.config)
+ || ((ssd->max_file_depth == -1) && DCE2_ScSmbFileDepth(
+ (dce2SmbProtoConf*)ssd->sd.config) == -1)))
{
- dce_alert(GID_DCE2, DCE2_SMB_NB_LT_COM, (dce2CommonStats*)&dce2_smb_stats);
- com_info.cmd_error |= DCE2_SMB_COM_ERROR__BAD_LENGTH;
+ DebugFormat(DEBUG_DCE_SMB, "Not inserting TID (%u) "
+ "because it's not IPC and not inspecting normal file "
+ "data.", tid);
return;
}
- const SmbCommon* sc = (SmbCommon*)nb_ptr;
- com_info.word_count = SmbWct(sc);
-
- // Make sure the word count is a valid one for the command. If not
- // testing shows an error will be returned. And command structures
- // won't lie on data correctly and out of bounds data accesses are possible.
- if (!DCE2_SmbIsValidWordCount(smb_com, (uint8_t)com_info.smb_type, com_info.word_count))
+ if (is_ipc && DCE2_ScSmbFileInspectionOnly((dce2SmbProtoConf*)ssd->sd.config))
{
- dce_alert(GID_DCE2, DCE2_SMB_BAD_WCT, (dce2CommonStats*)&dce2_smb_stats);
- com_info.cmd_error |= DCE2_SMB_COM_ERROR__INVALID_WORD_COUNT;
+ DebugFormat(DEBUG_DCE_SMB, "Not inserting TID (%u) "
+ "because it's IPC and only inspecting normal file "
+ "data.", tid);
return;
}
- // This gets the size of the SMB command from word count through byte count
- // using the advertised value in the word count field.
- com_info.cmd_size = (uint16_t)SMB_COM_SIZE(com_info.word_count);
- if (nb_len < com_info.cmd_size)
+ DebugFormat(DEBUG_DCE_SMB, "Inserting Tid: %u\n", tid);
+ int insert_tid = (int)tid;
+ // Set a bit so as to distinguish between IPC and non-IPC TIDs
+ if (!is_ipc)
+ insert_tid |= (1 << 16);
+
+ if (ssd->tid == DCE2_SENTINEL)
{
- dce_alert(GID_DCE2, DCE2_SMB_NB_LT_COM, (dce2CommonStats*)&dce2_smb_stats);
- com_info.cmd_error |= DCE2_SMB_COM_ERROR__BAD_LENGTH;
- return;
+ ssd->tid = insert_tid;
}
-
- uint16_t smb_bcc = SmbBcc(nb_ptr, com_info.cmd_size);
-
- // SMB_COM_NT_CREATE_ANDX is a special case. Who know what's going
- // on with the word count (see MS-CIFS and MS-SMB). A 42 word count
- // command seems to actually have 50 words, so who knows where the
- // byte count is. Just set to zero since it's not needed.
- if ((smb_com == SMB_COM_NT_CREATE_ANDX)
- && (com_info.smb_type == SMB_TYPE__RESPONSE))
- smb_bcc = 0;
-
- // If byte count is deemed invalid, alert but continue processing
- switch (smb_com)
+ else
{
- // Interim responses
- case SMB_COM_TRANSACTION:
- case SMB_COM_TRANSACTION2:
- case SMB_COM_NT_TRANSACT:
- // If word count is 0, byte count must be 0
- if ((com_info.word_count == 0) && (com_info.smb_type == SMB_TYPE__RESPONSE))
+ if (ssd->tids == nullptr)
{
- if (smb_bcc != 0)
+ ssd->tids = DCE2_ListNew(DCE2_LIST_TYPE__SPLAYED, DCE2_SmbUidTidFidCompare,
+ nullptr, nullptr, DCE2_LIST_FLAG__NO_DUPS);
+
+ if (ssd->tids == nullptr)
{
- dce_alert(GID_DCE2, DCE2_SMB_BAD_BCC, (dce2CommonStats*)&dce2_smb_stats);
- com_info.cmd_error |= DCE2_SMB_COM_ERROR__INVALID_BYTE_COUNT;
+ return;
}
- break;
- }
- // Fall through
- default:
- if (!DCE2_SmbIsValidByteCount(smb_com, (uint8_t)com_info.smb_type, smb_bcc))
- {
- dce_alert(GID_DCE2, DCE2_SMB_BAD_BCC, (dce2CommonStats*)&dce2_smb_stats);
- com_info.cmd_error |= DCE2_SMB_COM_ERROR__INVALID_BYTE_COUNT;
}
- break;
+
+ DCE2_ListInsert(ssd->tids, (void*)(uintptr_t)tid, (void*)(uintptr_t)insert_tid);
}
+}
- // Move just past byte count field which is the end of the command
- DCE2_MOVE(nb_ptr, nb_len, com_info.cmd_size);
+static DCE2_SmbRequestTracker* DCE2_SmbFindRequestTracker(DCE2_SmbSsnData* ssd,
+ const SmbNtHdr* smb_hdr)
+{
+ uint16_t uid = SmbUid(smb_hdr);
+ uint16_t tid = SmbTid(smb_hdr);
+ uint16_t pid = SmbPid(smb_hdr);
+ uint16_t mid = SmbMid(smb_hdr);
- // Validate that there is enough data to be able to process the command
- if (nb_len < DCE2_SmbGetMinByteCount(smb_com, (uint8_t)com_info.smb_type))
- {
- dce_alert(GID_DCE2, DCE2_SMB_NB_LT_BCC, (dce2CommonStats*)&dce2_smb_stats);
- com_info.cmd_error |= DCE2_SMB_COM_ERROR__BAD_LENGTH;
- }
+ Profile profile(dce2_smb_pstat_smb_req);
- // The byte count seems to be ignored by Windows and current Samba (3.5.4)
- // as long as it is less than the amount of data left. If more, an error
- // is returned.
- // !!!WARNING!!! the byte count should probably never be used.
- if (smb_bcc > nb_len)
- {
- dce_alert(GID_DCE2, DCE2_SMB_NB_LT_BCC, (dce2CommonStats*)&dce2_smb_stats);
- // Large byte count doesn't seem to matter for early Samba
- switch (DCE2_SsnGetPolicy(&ssd->sd))
- {
- case DCE2_POLICY__SAMBA_3_0_20:
- case DCE2_POLICY__SAMBA_3_0_22:
- case DCE2_POLICY__SAMBA_3_0_37:
- break;
- default:
- com_info.cmd_error |= DCE2_SMB_COM_ERROR__BAD_LENGTH;
- break;
- }
- }
- else if ((smb_bcc == 0) && (SmbCom(smb_hdr) == SMB_COM_TRANSACTION)
- && (DCE2_SmbType(ssd) == SMB_TYPE__REQUEST)
- && (DCE2_SsnGetPolicy(&ssd->sd) == DCE2_POLICY__SAMBA))
+ DebugFormat(DEBUG_DCE_SMB, "Find request tracker => "
+ "Uid: %u, Tid: %u, Pid: %u, Mid: %u ... ", uid, tid, pid, mid);
+
+ DCE2_SmbRequestTracker* tmp_rtracker = &ssd->rtracker;
+ int smb_com = SmbCom(smb_hdr);
+ switch (smb_com)
{
- // Current Samba errors on a zero byte count Transaction because it
- // uses it to get the Name string and if zero Name will be NULL and
- // it won't process it.
- com_info.cmd_error |= DCE2_SMB_COM_ERROR__BAD_LENGTH;
+ case SMB_COM_TRANSACTION_SECONDARY:
+ smb_com = SMB_COM_TRANSACTION;
+ break;
+ case SMB_COM_TRANSACTION2_SECONDARY:
+ smb_com = SMB_COM_TRANSACTION2;
+ break;
+ case SMB_COM_NT_TRANSACT_SECONDARY:
+ smb_com = SMB_COM_NT_TRANSACT;
+ break;
+ case SMB_COM_WRITE_COMPLETE:
+ smb_com = SMB_COM_WRITE_RAW;
+ break;
+ default:
+ break;
}
- com_info.byte_count = smb_bcc;
-}
+ DCE2_SmbRequestTracker* first_rtracker = nullptr;
+ DCE2_SmbRequestTracker* win_rtracker = nullptr;
+ DCE2_SmbRequestTracker* first_mid_rtracker = nullptr;
+ DCE2_SmbRequestTracker* ret_rtracker = nullptr;
+ while (tmp_rtracker != nullptr)
+ {
+ if ((tmp_rtracker->mid == (int)mid) && (tmp_rtracker->smb_com == smb_com))
+ {
+ // This is the normal case except for SessionSetupAndX and
+ // TreeConnect/TreeConnectAndX which will fall into the
+ // default case below.
+ if ((tmp_rtracker->pid == pid) && (tmp_rtracker->uid == uid)
+ && (tmp_rtracker->tid == tid))
+ {
+ ret_rtracker = tmp_rtracker;
+ }
+ else
+ {
+ switch (smb_com)
+ {
+ case SMB_COM_TRANSACTION:
+ case SMB_COM_TRANSACTION2:
+ case SMB_COM_NT_TRANSACT:
+ case SMB_COM_TRANSACTION_SECONDARY:
+ case SMB_COM_TRANSACTION2_SECONDARY:
+ case SMB_COM_NT_TRANSACT_SECONDARY:
+ // These should conform to above
+ break;
+ default:
+ if (tmp_rtracker->pid == pid)
+ ret_rtracker = tmp_rtracker;
+ break;
+ }
+ }
-/********************************************************************
- * Function: DCE2_SmbProcessCommand()
- *
- * Purpose:
- * This is the main function for handling SMB commands and command
- * chaining.
- * It does an initial check of the command to determine validity
- * and gets basic information about the command. Then it calls the
- * specific command function (setup in DCE2_SmbInitGlobals).
- * If there is command chaining, it will do the chaining foo to
- * get to the next command.
- *
- * Arguments:
- * DCE2_SmbSsnData * - pointer to session data structure
- * SmbNtHdr * - pointer to the SMB header structure
- * uint8_t * - current pointer to data, i.e. the command
- * uint32_t - the remaining length
- *
- * Returns: None
- *
- ********************************************************************/
-static void DCE2_SmbProcessCommand(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
- const uint8_t* nb_ptr, uint32_t nb_len)
-{
- uint8_t smb_com = SmbCom(smb_hdr);
+ if (ret_rtracker != nullptr)
+ {
+ DebugMessage(DEBUG_DCE_SMB, "Found.\n");
+ return ret_rtracker;
+ }
- while (nb_len > 0)
- {
- // Break out if command not supported
- if (smb_com_funcs[smb_com] == nullptr)
- break;
+ // Take the first one where the PIDs also match
+ // in the case of the Transacts above
+ if ((tmp_rtracker->pid == pid) && (win_rtracker == nullptr))
+ win_rtracker = tmp_rtracker;
- if (smb_deprecated_coms[smb_com])
- {
- dce_alert(GID_DCE2, DCE2_SMB_DEPR_COMMAND_USED, (dce2CommonStats*)&dce2_smb_stats);
+ // Set this to the first matching request in the queue
+ // where the Mid matches. Don't set for Windows if from
+ // client since PID/MID are necessary
+ if (((DCE2_SmbType(ssd) == SMB_TYPE__RESPONSE)
+ || !DCE2_SsnIsWindowsPolicy(&ssd->sd))
+ && first_mid_rtracker == nullptr)
+ {
+ first_mid_rtracker = tmp_rtracker;
+ }
}
- if (smb_unusual_coms[smb_com])
- {
- dce_alert(GID_DCE2, DCE2_SMB_UNUSUAL_COMMAND_USED, (dce2CommonStats*)&dce2_smb_stats);
- }
+ // Set the first one we see for early Samba versions
+ if ((first_rtracker == nullptr) && (tmp_rtracker->mid != DCE2_SENTINEL)
+ && (tmp_rtracker->smb_com == smb_com))
+ first_rtracker = tmp_rtracker;
- DCE2_SmbComInfo com_info;
- com_info.smb_type = DCE2_SmbType(ssd);
- com_info.cmd_error = DCE2_SMB_COM_ERROR__COMMAND_OK;
- com_info.word_count = 0;
- com_info.smb_com = smb_com;
- com_info.cmd_size = 0;
- com_info.byte_count = 0;
- DCE2_SmbCheckCommand(ssd, smb_hdr, smb_com, nb_ptr, nb_len, com_info);
+ // Look at the next request in the queue
+ if (tmp_rtracker == &ssd->rtracker)
+ tmp_rtracker = (DCE2_SmbRequestTracker*)DCE2_QueueFirst(ssd->rtrackers);
+ else
+ tmp_rtracker = (DCE2_SmbRequestTracker*)DCE2_QueueNext(ssd->rtrackers);
+ }
- // FIXIT-M port the rest
- return;
+ DCE2_Policy policy = DCE2_SsnGetPolicy(&ssd->sd);
+ switch (policy)
+ {
+ case DCE2_POLICY__SAMBA_3_0_20:
+ case DCE2_POLICY__SAMBA_3_0_22:
+ ret_rtracker = first_rtracker;
+ break;
+ case DCE2_POLICY__SAMBA:
+ case DCE2_POLICY__SAMBA_3_0_37:
+ ret_rtracker = first_mid_rtracker;
+ break;
+ case DCE2_POLICY__WIN2000:
+ case DCE2_POLICY__WINXP:
+ case DCE2_POLICY__WINVISTA:
+ case DCE2_POLICY__WIN2003:
+ case DCE2_POLICY__WIN2008:
+ case DCE2_POLICY__WIN7:
+ if (win_rtracker != nullptr)
+ ret_rtracker = win_rtracker;
+ else
+ ret_rtracker = first_mid_rtracker;
+ break;
+ default:
+ DebugFormat(DEBUG_DCE_SMB, "%s(%d) Invalid policy: %d",
+ __FILE__, __LINE__, policy);
+ break;
}
+
+ return ret_rtracker;
}
-/********************************************************************
- * Function: DCE2_SmbInspect()
- *
- * Purpose:
- * Determines whether the SMB command is something the preprocessor
- * needs to inspect.
- * This function returns a DCE2_SmbRequestTracker which tracks command
- * requests / responses.
- *
- * Arguments:
- * DCE2_SmbSsnData * - the session data structure.
- * const SmbNtHdr * - pointer to the SMB header.
- *
- * Returns:
- * DCE2_SmbRequestTracker * - nullptr if it's not something we want to or can
- * inspect.
- * Otherwise an initialized structure if request
- * and the found structure if response.
- *
- ********************************************************************/
-static DCE2_SmbRequestTracker* DCE2_SmbInspect(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr)
+static DCE2_SmbRequestTracker* DCE2_SmbNewRequestTracker(DCE2_SmbSsnData* ssd,
+ const SmbNtHdr* smb_hdr)
{
- int smb_com = SmbCom(smb_hdr);
+ uint16_t pid = SmbPid(smb_hdr);
+ uint16_t mid = SmbMid(smb_hdr);
+ uint16_t uid = SmbUid(smb_hdr);
+ uint16_t tid = SmbTid(smb_hdr);
- DebugFormat(DEBUG_DCE_SMB, "SMB command: %s (0x%02X)\n",
- smb_com_strings[smb_com], smb_com);
+ Profile profile(dce2_smb_pstat_smb_req);
- if (smb_com_funcs[smb_com] == nullptr)
+ if (ssd == nullptr)
{
- DebugMessage(DEBUG_DCE_SMB, "Command isn't processed "
- "by preprocessor.\n");
return nullptr;
}
- // See if this is something we need to inspect
- DCE2_Policy policy = DCE2_SsnGetServerPolicy(&ssd->sd);
+ if (ssd->outstanding_requests >= ssd->max_outstanding_requests)
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_MAX_REQS_EXCEEDED, (dce2CommonStats*)&dce2_smb_stats);
+ }
+
+ // Check for outstanding requests with the same MID
+ DCE2_SmbRequestTracker* tmp_rtracker = &ssd->rtracker;
+ while ((tmp_rtracker != nullptr) && (tmp_rtracker->mid != DCE2_SENTINEL))
+ {
+ if (tmp_rtracker->mid == (int)mid)
+ {
+ // Have yet to see an MID repeatedly used so shouldn't
+ // be any outstanding requests with the same MID.
+ dce_alert(GID_DCE2, DCE2_SMB_REQS_SAME_MID, (dce2CommonStats*)&dce2_smb_stats);
+ break;
+ }
+
+ // Look at the next request in the queue
+ if (tmp_rtracker == &ssd->rtracker)
+ tmp_rtracker = (DCE2_SmbRequestTracker*)DCE2_QueueFirst(ssd->rtrackers);
+ else
+ tmp_rtracker = (DCE2_SmbRequestTracker*)DCE2_QueueNext(ssd->rtrackers);
+ }
+
DCE2_SmbRequestTracker* rtracker = nullptr;
- if (DCE2_SmbType(ssd) == SMB_TYPE__REQUEST)
+ if (ssd->rtracker.mid == DCE2_SENTINEL)
{
- switch (smb_com)
+ rtracker = &ssd->rtracker;
+ }
+ else
+ {
+ if (ssd->rtrackers == nullptr)
{
- case SMB_COM_NEGOTIATE:
- if (ssd->ssn_state_flags & DCE2_SMB_SSN_STATE__NEGOTIATED)
+ ssd->rtrackers = DCE2_QueueNew(DCE2_SmbRequestTrackerDataFree);
+ if (ssd->rtrackers == nullptr)
{
- dce_alert(GID_DCE2, DCE2_SMB_MULTIPLE_NEGOTIATIONS,
- (dce2CommonStats*)&dce2_smb_stats);
return nullptr;
}
- break;
- case SMB_COM_SESSION_SETUP_ANDX:
- break;
- case SMB_COM_TREE_CONNECT:
- case SMB_COM_TREE_CONNECT_ANDX:
- case SMB_COM_RENAME:
- case SMB_COM_LOGOFF_ANDX:
- // FIXIT-M - uncomment after the code to insert uid is ported
- // till then - will always fail
- //if (DCE2_SmbFindUid(ssd, SmbUid(smb_hdr)) != DCE2_RET__SUCCESS)
- // return nullptr;
- break;
- default:
- // FIXIT-M - uncomment after the code to insert tid is ported
- // till then - will always fail
- /*
- if (DCE2_SmbFindTid(ssd, SmbTid(smb_hdr)) != DCE2_RET__SUCCESS)
- {
- DebugFormat(DEBUG_DCE_SMB,
- "Couldn't find Tid (%u)\n", SmbTid(smb_hdr));
- return nullptr;
- }
- */
+ }
- if (DCE2_SmbIsTidIPC(ssd, SmbTid(smb_hdr)))
- {
- switch (smb_com)
- {
- case SMB_COM_OPEN:
- case SMB_COM_CREATE:
- case SMB_COM_CREATE_NEW:
- case SMB_COM_WRITE_AND_CLOSE:
- case SMB_COM_WRITE_AND_UNLOCK:
- case SMB_COM_READ:
- // Samba doesn't allow these commands under an IPC tree
- switch (policy)
- {
- case DCE2_POLICY__SAMBA:
- case DCE2_POLICY__SAMBA_3_0_37:
- case DCE2_POLICY__SAMBA_3_0_22:
- case DCE2_POLICY__SAMBA_3_0_20:
- DebugMessage(DEBUG_DCE_SMB, "Samba doesn't "
- "process this command under an IPC tree.\n");
- return nullptr;
- default:
- break;
- }
- break;
- case SMB_COM_READ_RAW:
- case SMB_COM_WRITE_RAW:
- // Samba and Windows Vista on don't allow these commands
- // under an IPC tree, whether or not the raw read/write
- // flag is set in the Negotiate capabilities.
- // Windows RSTs the connection and Samba FINs it.
- switch (policy)
- {
- case DCE2_POLICY__WINVISTA:
- case DCE2_POLICY__WIN2008:
- case DCE2_POLICY__WIN7:
- case DCE2_POLICY__SAMBA:
- case DCE2_POLICY__SAMBA_3_0_37:
- case DCE2_POLICY__SAMBA_3_0_22:
- case DCE2_POLICY__SAMBA_3_0_20:
- DebugMessage(DEBUG_DCE_SMB, "Samba and "
- "Windows Vista on don't process this "
- "command under an IPC tree.\n");
- return nullptr;
- default:
- break;
- }
- break;
- case SMB_COM_LOCK_AND_READ:
- // The lock will fail so the read won't happen
- return nullptr;
- default:
- break;
- }
- }
- else // Not IPC
- {
- switch (smb_com)
- {
- // These commands are only used for IPC
- case SMB_COM_TRANSACTION:
- case SMB_COM_TRANSACTION_SECONDARY:
- DebugMessage(DEBUG_DCE_SMB, "secondary transaction not IPC.\n");
- return nullptr;
- case SMB_COM_READ_RAW:
- case SMB_COM_WRITE_RAW:
- // Windows Vista on don't seem to support these
- // commands, whether or not the raw read/write
- // flag is set in the Negotiate capabilities.
- // Windows RSTs the connection.
- switch (policy)
- {
- case DCE2_POLICY__WINVISTA:
- case DCE2_POLICY__WIN2008:
- case DCE2_POLICY__WIN7:
- DebugMessage(DEBUG_DCE_SMB,
- "Windows Vista on don't process "
- "this command.\n");
- return nullptr;
- default:
- break;
- }
- break;
- default:
- break;
- }
- }
- break;
+ rtracker = (DCE2_SmbRequestTracker*)SnortAlloc(sizeof(DCE2_SmbRequestTracker));
+ if (rtracker == nullptr)
+ {
+ return nullptr;
}
- switch (smb_com)
+ if (DCE2_QueueEnqueue(ssd->rtrackers, (void*)rtracker) != DCE2_RET__SUCCESS)
{
- case SMB_COM_TRANSACTION_SECONDARY:
- case SMB_COM_TRANSACTION2_SECONDARY:
- case SMB_COM_NT_TRANSACT_SECONDARY:
- rtracker = DCE2_SmbFindRequestTracker(ssd, smb_hdr);
- break;
- case SMB_COM_TRANSACTION:
- case SMB_COM_TRANSACTION2:
- case SMB_COM_NT_TRANSACT:
- // If there is already and existing request tracker
- // and the transaction is not complete, server will
- // return an error.
- rtracker = DCE2_SmbFindRequestTracker(ssd, smb_hdr);
- if (rtracker != nullptr)
- break;
- // Fall through
- default:
- rtracker = DCE2_SmbNewRequestTracker(ssd, smb_hdr);
- break;
+ free((void*)rtracker);
+ return nullptr;
}
}
- else
- {
- rtracker = DCE2_SmbFindRequestTracker(ssd, smb_hdr);
- }
+
+ rtracker->smb_com = SmbCom(smb_hdr);
+ rtracker->uid = uid;
+ rtracker->tid = tid;
+ rtracker->pid = pid;
+ rtracker->mid = (int)mid;
+ memset(&rtracker->ttracker, 0, sizeof(rtracker->ttracker));
+ rtracker->ftracker = nullptr;
+ rtracker->sequential_only = false;
+
+ ssd->outstanding_requests++;
+ if (ssd->outstanding_requests > dce2_smb_stats.smb_max_outstanding_requests)
+ dce2_smb_stats.smb_max_outstanding_requests = ssd->outstanding_requests;
+
+ DebugFormat(DEBUG_DCE_SMB, "Added new request tracker => "
+ "Uid: %u, Tid: %u, Pid: %u, Mid: %u\n",
+ rtracker->uid, rtracker->tid, rtracker->pid, rtracker->mid);
+ DebugFormat(DEBUG_DCE_SMB,
+ "Current outstanding requests: %u\n", ssd->outstanding_requests);
return rtracker;
}
-// Temporary command function placeholder, until all of them are ported
-DCE2_Ret DCE2_SmbComFuncPlaceholder(DCE2_SmbSsnData*, const SmbNtHdr*,
- const DCE2_SmbComInfo*, const uint8_t*, uint32_t)
+static DCE2_Ret DCE2_SmbProcessRequestData(DCE2_SmbSsnData* ssd,
+ const uint16_t fid, const uint8_t* data_ptr, uint32_t data_len, uint64_t offset)
{
- return DCE2_RET__SUCCESS;
-}
+ DCE2_SmbFileTracker* ftracker = DCE2_SmbGetFileTracker(ssd, fid);
-static bool DCE2_SmbAutodetect(Packet* p)
-{
- if (p->dsize > (sizeof(NbssHdr) + sizeof(SmbNtHdr)))
- {
- NbssHdr* nb_hdr = (NbssHdr*)p->data;
- SmbNtHdr* smb_hdr = (SmbNtHdr*)(p->data + sizeof(NbssHdr));
+ DebugFormat(DEBUG_DCE_SMB,
+ "Entering Processing request data with Fid: 0x%04X, ftracker ? %s ~~~~~~~~~~~~~~~~~\n",
+ fid,ftracker ? "TRUE" : "FALSE");
+ if (ftracker == nullptr)
+ return DCE2_RET__ERROR;
- if ((SmbId(smb_hdr) != DCE2_SMB_ID)
- && (SmbId(smb_hdr) != DCE2_SMB2_ID))
- {
- return false;
- }
+ DebugFormat(DEBUG_DCE_SMB,
+ "Processing request data with Fid: 0x%04X, is_ipc ? %s ~~~~~~~~~~~~~~~~~\n", ftracker->fid,
+ ftracker->is_ipc ? "TRUE" : "FALSE");
- switch (NbssType(nb_hdr))
- {
- // FIXIT-L currently all ports are treated as autodetect.
- // On port 139, there is always an initial Session Request / Session Positive/Negative
- // response.
- // These message types were added , to make sure port 139 is treated as smb.
- // Remove once detect/autodetect supported.
- case NBSS_SESSION_TYPE__REQUEST:
- if (DCE2_SsnFromClient(p))
- return true;
- break;
+ // Set this in case of chained commands or reassembled packet
+ ssd->cur_rtracker->ftracker = ftracker;
- case NBSS_SESSION_TYPE__POS_RESPONSE:
- case NBSS_SESSION_TYPE__NEG_RESPONSE:
- if (DCE2_SsnFromServer(p))
- return true;
- break;
+ if (ftracker->is_ipc)
+ {
+ // Maximum possible fragment length is 16 bit
+ if (data_len > UINT16_MAX)
+ data_len = UINT16_MAX;
- case NBSS_SESSION_TYPE__MESSAGE:
- return true;
- break;
- default:
- break;
- }
+ DCE2_CoProcess(&ssd->sd, ftracker->fp_co_tracker, data_ptr, (uint16_t)data_len);
+
+ if (!ftracker->fp_used)
+ ftracker->fp_used = true;
+ }
+ else
+ {
+ ftracker->ff_file_offset = offset;
+ // FIXIT-M uncomment when file processing is ported
+ // DCE2_SmbProcessFileData(ssd, ftracker, data_ptr, data_len, true);
}
- return false;
+ DebugMessage(DEBUG_DCE_SMB, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
+
+ return DCE2_RET__SUCCESS;
}
-void DCE2_SmbDataFree(DCE2_SmbSsnData* ssd)
+static DCE2_Ret DCE2_SmbProcessResponseData(DCE2_SmbSsnData* ssd,
+ const uint8_t* data_ptr, uint32_t data_len)
{
- if (ssd == nullptr)
- return;
-
- // FIXIT This tries to account for the situation where we never knew the file
- // size and the TCP session was shutdown before an SMB_COM_CLOSE on the file.
- // Possibly need to add callback to fileAPI since it may have already
- // released it's resources.
- //DCE2_SmbFinishFileAPI(ssd);
+ DCE2_SmbFileTracker* ftracker = ssd->cur_rtracker->ftracker;
- if (ssd->uids != nullptr)
- {
- DCE2_ListDestroy(ssd->uids);
- ssd->uids = nullptr;
- }
-
- if (ssd->tids != nullptr)
- {
- DCE2_ListDestroy(ssd->tids);
- ssd->tids = nullptr;
- }
+ if (ftracker == nullptr)
+ return DCE2_RET__ERROR;
- // FIXIT-M uncomment once file tracking is ported
-/*
- DCE2_SmbCleanFileTracker(&ssd->ftracker);
- if (ssd->ftrackers != nullptr)
- {
- DCE2_ListDestroy(ssd->ftrackers);
- ssd->ftrackers = nullptr;
- }
-*/
+ DebugFormat(DEBUG_DCE_SMB,
+ "Processing response data with Fid: 0x%04X ~~~~~~~~~~~~~~~~\n", ftracker->fid);
- DCE2_SmbCleanRequestTracker(&ssd->rtracker);
- if (ssd->rtrackers != nullptr)
+ if (ftracker->is_ipc)
{
- DCE2_QueueDestroy(ssd->rtrackers);
- ssd->rtrackers = nullptr;
- }
+ // Maximum possible fragment length is 16 bit
+ if (data_len > UINT16_MAX)
+ data_len = UINT16_MAX;
- if (ssd->cli_seg != nullptr)
- {
- DCE2_BufferDestroy(ssd->cli_seg);
- ssd->cli_seg = nullptr;
+ DCE2_CoProcess(&ssd->sd, ftracker->fp_co_tracker, data_ptr, (uint16_t)data_len);
}
-
- if (ssd->srv_seg != nullptr)
+ else
{
- DCE2_BufferDestroy(ssd->srv_seg);
- ssd->srv_seg = nullptr;
+ ftracker->ff_file_offset = ssd->cur_rtracker->file_offset;
+ // FIXIT-M uncomment when file processing is ported
+ //DCE2_SmbProcessFileData(ssd, ftracker, data_ptr, data_len, false);
}
-}
-Dce2SmbFlowData::Dce2SmbFlowData() : FlowData(flow_id)
-{
-}
+ DebugMessage(DEBUG_DCE_SMB, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
-Dce2SmbFlowData::~Dce2SmbFlowData()
-{
- DCE2_SmbDataFree(&dce2_smb_session);
+ return DCE2_RET__SUCCESS;
}
-unsigned Dce2SmbFlowData::flow_id = 0;
-
-DCE2_SmbSsnData* get_dce2_smb_session_data(Flow* flow)
-{
- Dce2SmbFlowData* fd = (Dce2SmbFlowData*)flow->get_application_data(
- Dce2SmbFlowData::flow_id);
-
- return fd ? &fd->dce2_smb_session : nullptr;
-}
-
-static DCE2_SmbSsnData* set_new_dce2_smb_session(Packet* p)
-{
- Dce2SmbFlowData* fd = new Dce2SmbFlowData;
-
- memset(&fd->dce2_smb_session,0,sizeof(DCE2_SmbSsnData));
- p->flow->set_application_data(fd);
- return(&fd->dce2_smb_session);
-}
-
-static DCE2_SmbSsnData* dce2_create_new_smb_session(Packet* p, dce2SmbProtoConf* config)
+/********************************************************************
+ * Function: DCE2_SmbHdrChecks()
+ *
+ * Checks some relevant fields in the header to make sure they're
+ * sane.
+ * Side effects are potential alerts for anomolous behavior.
+ *
+ * Arguments:
+ * DCE2_SmbSsnData *
+ * Pointer to the session data structure.
+ * SmbNtHdr *
+ * Pointer to the header struct layed over the packet data.
+ *
+ * Returns:
+ * DCE2_Ret
+ * DCE2_RET__IGNORE if we should continue processing, but
+ * ignore data because of the error.
+ * DCE2_RET__SUCCESS if we should continue processing.
+ *
+ ********************************************************************/
+static DCE2_Ret DCE2_SmbHdrChecks(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr)
{
- DCE2_SmbSsnData* dce2_smb_sess = nullptr;
- Profile profile(dce2_smb_pstat_new_session);
-
- //FIXIT-M Re-evaluate after infrastructure/binder support if autodetect here
- //is necessary
+ Packet* p = ssd->sd.wire_pkt;
- if (DCE2_SmbAutodetect(p))
+ if ((DCE2_SsnFromServer(p) && (SmbType(smb_hdr) == SMB_TYPE__REQUEST)) ||
+ (DCE2_SsnFromClient(p) && (SmbType(smb_hdr) == SMB_TYPE__RESPONSE)))
{
- DebugMessage(DEBUG_DCE_SMB, "DCE over SMB packet detected\n");
- DebugMessage(DEBUG_DCE_SMB, "Creating new session\n");
-
- 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 = 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.ropts);
-
- dce2_smb_stats.smb_sessions++;
- DebugFormat(DEBUG_DCE_SMB,"Created (%p)\n", (void*)dce2_smb_sess);
-
- 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.wire_pkt = p;
- dce2_smb_sess->sd.config = (void*)config;
-
- DCE2_SsnSetAutodetected(&dce2_smb_sess->sd, p);
- }
+ // FIXIT-M port segment check
+ // Same for all cases below
+ dce_alert(GID_DCE2, DCE2_SMB_BAD_TYPE, (dce2CommonStats*)&dce2_smb_stats);
+ // Continue looking at traffic. Neither Windows nor Samba seem
+ // to care, or even look at this flag
}
- return dce2_smb_sess;
-}
-
-static DCE2_SmbSsnData* dce2_handle_smb_session(Packet* p, dce2SmbProtoConf* config)
-{
- Profile profile(dce2_smb_pstat_session);
-
- DCE2_SmbSsnData* dce2_smb_sess = get_dce2_smb_session_data(p->flow);
-
- if (dce2_smb_sess == nullptr)
- {
- dce2_smb_sess = dce2_create_new_smb_session(p, config);
- }
- else
+ if ((SmbId(smb_hdr) != DCE2_SMB_ID)
+ && (SmbId(smb_hdr) != DCE2_SMB2_ID))
{
- DCE2_SsnData* sd = (DCE2_SsnData*)dce2_smb_sess;
- sd->wire_pkt = p;
-
- if (DCE2_SsnAutodetected(sd) && !(p->packet_flags & sd->autodetect_dir))
- {
- /* Try to autodetect in opposite direction */
- if (!DCE2_SmbAutodetect(p))
- {
- DebugMessage(DEBUG_DCE_SMB, "Bad autodetect.\n");
- DCE2_SsnNoInspect(sd);
- dce2_smb_stats.sessions_aborted++;
- dce2_smb_stats.bad_autodetects++;
- return nullptr;
- }
- DCE2_SsnClearAutodetected(sd);
- }
+ dce_alert(GID_DCE2, DCE2_SMB_BAD_ID, (dce2CommonStats*)&dce2_smb_stats);
+ return DCE2_RET__IGNORE;
}
- DebugFormat(DEBUG_DCE_SMB, "Session pointer: %p\n", (void*)dce2_smb_sess);
- // FIXIT-M add remaining session handling logic
+ return DCE2_RET__SUCCESS;
+}
- return dce2_smb_sess;
+/********************************************************************
+ * Function: DCE2_SmbGetMinByteCount()
+ *
+ * Purpose:
+ * Returns the minimum byte count for the given command request
+ * or response.
+ *
+ * Arguments:
+ * uint8_t - the SMB command code
+ * uint8_t - SMB_TYPE__REQUEST or SMB_TYPE__RESPONSE
+ *
+ * Returns:
+ * uint16_t - the minimum byte count
+ *
+ ********************************************************************/
+static inline uint16_t DCE2_SmbGetMinByteCount(uint8_t com, uint8_t resp)
+{
+ return smb_bccs[com][resp][0];
}
/********************************************************************
- * Function: DCE2_NbssHdrChecks()
+ * Function: DCE2_SmbGetString()
*
* Purpose:
- * Does validation of the NetBIOS header. SMB will only run over
- * the Session Message type. On port 139, there is always an
- * initial Session Request / Session Positive/Negative response
- * followed by the normal SMB conversation, i.e. Negotiate,
- * SessionSetupAndX, etc.
- * Side effects are potential alerts for anomolous behavior.
+ * Parses data passed in and returns an ASCII string. True
+ * unicode characters are replaced with a '.'
*
* Arguments:
- * DCE2_SmbSsnData * - the session data structure.
- * const NbssHdr * - pointer to the NetBIOS Session Service
- * header structure. Size is already validated.
+ * const uint8_t * - pointer to data
+ * uint32_t - data length
+ * bool - true if the data is unicode (UTF-16LE)
+ * bool - true if the function should only return the
+ * file name instead of the entire path
*
* Returns:
- * DCE2_Ret - DCE2_RET__SUCCESS if all goes well and processing
- * should continue.
- * DCE2_RET__IGNORE if it's not something we need to
- * look at.
- * DCE2_RET__ERROR if an invalid NetBIOS Session
- * Service type is found.
+ * char * - NULL terminated ASCII string
*
********************************************************************/
-static DCE2_Ret DCE2_NbssHdrChecks(DCE2_SmbSsnData* ssd, const NbssHdr* nb_hdr)
+static char* DCE2_SmbGetString(const uint8_t* data,
+ uint32_t data_len, bool unicode, bool get_file)
{
- Packet* p = ssd->sd.wire_pkt;
+ char* str;
+ uint32_t i, j, k = unicode ? data_len - 1 : data_len;
+ uint8_t inc = unicode ? 2 : 1;
- DebugMessage(DEBUG_DCE_SMB, "NetBIOS Session Service type: ");
+ if (data_len < inc)
+ return nullptr;
- switch (NbssType(nb_hdr))
+ // Move forward. Don't know if the end of data is actually
+ // the end of the string.
+ for (i = 0, j = 0; i < k; i += inc)
{
- case NBSS_SESSION_TYPE__MESSAGE:
- /* Only want to look at session messages */
- DebugMessage(DEBUG_DCE_SMB, "Session Message\n");
-
- if (!DCE2_SmbIsRawData(ssd))
- {
- uint32_t nb_len = NbssLen(nb_hdr);
-
- if (nb_len == 0)
- return DCE2_RET__IGNORE;
-
- if (nb_len < sizeof(SmbNtHdr))
- {
- DebugFormat(DEBUG_DCE_SMB, "NetBIOS SS len(%u) < SMB header len(%u).\n",
- sizeof(SmbNtHdr), sizeof(NbssHdr) + nb_len);
-
- // FIXIT-M port segment check
- // Same for all cases below
- dce_alert(GID_DCE2, DCE2_SMB_NB_LT_SMBHDR, (dce2CommonStats*)&dce2_smb_stats);
- return DCE2_RET__IGNORE;
- }
- }
-
- return DCE2_RET__SUCCESS;
-
- case NBSS_SESSION_TYPE__REQUEST:
- DebugMessage(DEBUG_DCE_SMB, "Session Request\n");
- if (DCE2_SsnFromServer(p))
- {
- dce_alert(GID_DCE2, DCE2_SMB_BAD_NBSS_TYPE, (dce2CommonStats*)&dce2_smb_stats);
- }
-
- break;
-
- case NBSS_SESSION_TYPE__POS_RESPONSE:
- case NBSS_SESSION_TYPE__NEG_RESPONSE:
- case NBSS_SESSION_TYPE__RETARGET_RESPONSE:
- if (DCE2_SsnFromClient(p))
- {
- dce_alert(GID_DCE2, DCE2_SMB_BAD_NBSS_TYPE, (dce2CommonStats*)&dce2_smb_stats);
- }
+ uint16_t uchar = unicode ? extract_16bits(data + i) : data[i];
- break;
+ if (uchar == 0)
+ break;
+ else if (get_file && ((uchar == 0x002F) || (uchar == 0x005C))) // slash and back-slash
+ j = i + inc;
+ }
- case NBSS_SESSION_TYPE__KEEP_ALIVE:
- DebugMessage(DEBUG_DCE_SMB, "Session Keep Alive\n");
- break;
+ // Only got a NULL byte or nothing after slash/back-slash or too big.
+ if ((i == 0) || (j == i)
+ || (get_file && (i > DCE2_SMB_MAX_COMP_LEN))
+ || (i > DCE2_SMB_MAX_PATH_LEN))
+ return nullptr;
- default:
- DebugFormat(DEBUG_DCE_SMB,
- "Invalid Session Service type: 0x%02X\n", NbssType(nb_hdr));
- dce_alert(GID_DCE2, DCE2_SMB_BAD_NBSS_TYPE, (dce2CommonStats*)&dce2_smb_stats);
+ str = (char*)SnortAlloc(((i-j)>>(inc-1))+1);
+ if (str == nullptr)
+ return nullptr;
- return DCE2_RET__ERROR;
+ for (k = 0; j < i; j += inc, k++)
+ {
+ if (isprint((int)data[j]))
+ str[k] = (char)data[j];
+ else
+ str[k] = '.';
}
- return DCE2_RET__IGNORE;
+ str[k] = 0;
+
+ return str;
}
/********************************************************************
- * Function: DCE2_SmbProcess()
+ * Function: DCE2_SmbCheckCommand()
*
* Purpose:
- * This is the main entry point for SMB processing.
+ * Checks basic validity of an SMB command.
*
* Arguments:
- * DCE2_SmbSsnData * - the session data structure.
+ * DCE2_SmbSsnData * - pointer to session data structure
+ * SmbNtHdr * - pointer to the SMB header structure
+ * uint8_t - the SMB command code, i.e. SMB_COM_*
+ * uint8_t * - current pointer to data, i.e. the command
+ * uint32_t - the remaining length
+ * DCE2_SmbComInfo & -
+ * Populated structure for command processing
*
* Returns: None
*
********************************************************************/
-void DCE2_SmbProcess(DCE2_SmbSsnData* ssd)
+static void DCE2_SmbCheckCommand(DCE2_SmbSsnData* ssd,
+ const SmbNtHdr* smb_hdr, const uint8_t smb_com,
+ const uint8_t* nb_ptr, uint32_t nb_len, DCE2_SmbComInfo& com_info)
{
- DebugMessage(DEBUG_DCE_SMB, "Processing SMB packet.\n");
- dce2_smb_stats.smb_pkts++;
-
- const Packet* p = ssd->sd.wire_pkt;
- const uint8_t* data_ptr = p->data;
- uint16_t data_len = p->dsize;
-
- /* Have to account for segmentation. Even though stream will give
- * us larger chunks, we might end up in the middle of something */
- while (data_len > 0)
+ // Check for server error response
+ if (com_info.smb_type == SMB_TYPE__RESPONSE)
{
- // We are ignoring an entire PDU or junk data so state should be NETBIOS_HEADER
- // Note that it could be TCP segmented so ignore_bytes could be greater than
- // the amount of data we have
- uint32_t* ignore_bytes = DCE2_SmbGetIgnorePtr(ssd);
- if (*ignore_bytes)
- {
- DebugFormat(DEBUG_DCE_SMB, "Ignoring %u bytes\n", *ignore_bytes);
+ const SmbEmptyCom* ec = (SmbEmptyCom*)nb_ptr;
- if (data_len <= *ignore_bytes)
- {
- *ignore_bytes -= data_len;
- return;
- }
- else
- {
- /* ignore bytes is less than UINT16_MAX */
- DCE2_MOVE(data_ptr, data_len, (uint16_t)*ignore_bytes);
- *ignore_bytes = 0;
- }
+ // Verify there is enough data to do checks
+ if (nb_len < sizeof(SmbEmptyCom))
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_NB_LT_COM, (dce2CommonStats*)&dce2_smb_stats);
+ com_info.cmd_error |= DCE2_SMB_COM_ERROR__BAD_LENGTH;
+ return;
}
- DCE2_SmbDataState* data_state = DCE2_SmbGetDataState(ssd);
- DCE2_SmbRequestTracker* rtracker = nullptr;
- switch (*data_state)
- {
- // This state is to verify it's a NetBIOS Session Message packet
- // and to get the length of the SMB PDU. Also does the SMB junk
- // data check. If it's not a Session Message the data isn't
- // processed since it won't be carrying SMB.
- case DCE2_SMB_DATA_STATE__NETBIOS_HEADER:
+ // If word and byte counts are zero and there is an error
+ // the server didn't accept client request
+ if ((SmbEmptyComWct(ec) == 0)
+ && (SmbEmptyComBcc(ec) == 0) && SmbError(smb_hdr))
{
- uint32_t data_need = sizeof(NbssHdr);
-
- // See if there is enough data to process the NetBIOS header
- if (data_len < data_need)
- {
- DebugFormat(DEBUG_DCE_SMB, "Data len(%u) < NetBIOS SS header(%u). "
- "Queueing data.\n", data_len, data_need);
-
- // FIXIT-M port segmentation code
- return;
- }
-
- // Set the NetBIOS header structure
- NbssHdr* nb_hdr = (NbssHdr*)data_ptr;
- uint32_t nb_len = NbssLen(nb_hdr);
-
- DebugFormat(DEBUG_DCE_SMB, "NetBIOS PDU length: %u\n", nb_len);
+ DebugFormat(DEBUG_DCE_SMB,
+ "Response error: 0x%08X\n", SmbNtStatus(smb_hdr));
- DCE2_Ret status = DCE2_NbssHdrChecks(ssd, nb_hdr);
- if (status != DCE2_RET__SUCCESS)
+ // If broken pipe, clean up data associated with open named pipe
+ if (SmbBrokenPipe(smb_hdr))
{
- DebugMessage(DEBUG_DCE_SMB, "Not a NetBIOS Session Message.\n");
-
- if (status == DCE2_RET__IGNORE)
- {
- DebugMessage(DEBUG_DCE_SMB, "Valid NetBIOS header "
- "type so ignoring NetBIOS length bytes.\n");
- *ignore_bytes = data_need + nb_len;
- }
- else // nb_ret == DCE2_RET__ERROR, i.e. invalid NetBIOS type
- {
- DebugMessage(DEBUG_DCE_SMB, "Not a valid NetBIOS "
- "header type so trying to find \\xffSMB to "
- "determine how many bytes to ignore.\n");
- *ignore_bytes = DCE2_IgnoreJunkData(data_ptr, data_len, data_need + nb_len);
- }
-
- dce2_smb_stats.smb_ignored_bytes += *ignore_bytes;
- continue;
+ DebugMessage(DEBUG_DCE_SMB, "Broken or disconnected pipe.\n");
+ DCE2_SmbRemoveFileTracker(ssd, ssd->cur_rtracker->ftracker);
}
- switch (ssd->pdu_state)
- {
- case DCE2_SMB_PDU_STATE__COMMAND:
- *data_state = DCE2_SMB_DATA_STATE__SMB_HEADER;
- break;
- case DCE2_SMB_PDU_STATE__RAW_DATA:
- *data_state = DCE2_SMB_DATA_STATE__NETBIOS_PDU;
- // Continue here because of fall through below
- continue;
- default:
- DebugFormat(DEBUG_DCE_SMB,"%s(%d) Invalid SMB PDU "
- "state: %d\n", __FILE__, __LINE__, ssd->pdu_state);
- return;
- }
+ com_info.cmd_error |= DCE2_SMB_COM_ERROR__STATUS_ERROR;
+ return;
}
+ }
- // Fall through for DCE2_SMB_DATA_STATE__SMB_HEADER
- // This is the normal progression without segmentation.
-
- // This state is to do validation checks on the SMB header and
- // more importantly verify it's data that needs to be inspected.
- // If the TID in the SMB header is not referring to the IPC share
- // there won't be any DCE/RPC traffic associated with it.
- case DCE2_SMB_DATA_STATE__SMB_HEADER:
- {
- // FIXIT-M add segmentation code path, including seg_buf code to the entire state
-
- uint32_t data_need = (sizeof(NbssHdr) + sizeof(SmbNtHdr));
- // See if there is enough data to process the SMB header
- if (data_len < data_need)
- {
- DebugFormat(DEBUG_DCE_SMB, "Data len (%u) < "
- "NetBIOS SS header + SMB header (%u). Queueing data.\n",
- data_len, data_need);
+ // Set the header size to the minimum size the command can be
+ // without the byte count to make sure there is enough data to
+ // get the word count.
+ SmbAndXCom andx_com = smb_chain_map[smb_com];
+ int chk_com_size;
+ if (andx_com == SMB_ANDX_COM__NONE)
+ chk_com_size = sizeof(SmbCommon);
+ else
+ chk_com_size = sizeof(SmbAndXCommon);
- // FIXIT-M add segmentation code path
- return;
- }
+ // Verify there is enough data to do checks
+ if (nb_len < (uint32_t)chk_com_size)
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_NB_LT_COM, (dce2CommonStats*)&dce2_smb_stats);
+ com_info.cmd_error |= DCE2_SMB_COM_ERROR__BAD_LENGTH;
+ return;
+ }
- // FIXIT-M add segmentation checks
- SmbNtHdr* smb_hdr = (SmbNtHdr*)(data_ptr + sizeof(NbssHdr));
+ const SmbCommon* sc = (SmbCommon*)nb_ptr;
+ com_info.word_count = SmbWct(sc);
- // FIXIT-L Don't support SMB2 yet
- if (SmbId(smb_hdr) == DCE2_SMB2_ID)
- {
- ssd->sd.flags |= DCE2_SSN_FLAG__NO_INSPECT;
- return;
- }
+ // Make sure the word count is a valid one for the command. If not
+ // testing shows an error will be returned. And command structures
+ // won't lie on data correctly and out of bounds data accesses are possible.
+ if (!DCE2_SmbIsValidWordCount(smb_com, (uint8_t)com_info.smb_type, com_info.word_count))
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_BAD_WCT, (dce2CommonStats*)&dce2_smb_stats);
+ com_info.cmd_error |= DCE2_SMB_COM_ERROR__INVALID_WORD_COUNT;
+ return;
+ }
- // See if this is something we need to inspect
- rtracker = DCE2_SmbInspect(ssd, smb_hdr);
- if (rtracker == nullptr)
- {
- DebugMessage(DEBUG_DCE_SMB, "Not inspecting SMB packet.\n");
+ // This gets the size of the SMB command from word count through byte count
+ // using the advertised value in the word count field.
+ com_info.cmd_size = (uint16_t)SMB_COM_SIZE(com_info.word_count);
+ if (nb_len < com_info.cmd_size)
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_NB_LT_COM, (dce2CommonStats*)&dce2_smb_stats);
+ com_info.cmd_error |= DCE2_SMB_COM_ERROR__BAD_LENGTH;
+ return;
+ }
- // FIXIT-M add segmentation
- *ignore_bytes = sizeof(NbssHdr) + NbssLen((NbssHdr*)data_ptr);
+ uint16_t smb_bcc = SmbBcc(nb_ptr, com_info.cmd_size);
- *data_state = DCE2_SMB_DATA_STATE__NETBIOS_HEADER;
- dce2_smb_stats.smb_ignored_bytes += *ignore_bytes;
- continue;
- }
+ // SMB_COM_NT_CREATE_ANDX is a special case. Who know what's going
+ // on with the word count (see MS-CIFS and MS-SMB). A 42 word count
+ // command seems to actually have 50 words, so who knows where the
+ // byte count is. Just set to zero since it's not needed.
+ if ((smb_com == SMB_COM_NT_CREATE_ANDX)
+ && (com_info.smb_type == SMB_TYPE__RESPONSE))
+ smb_bcc = 0;
- // Check the SMB header for anomolies
- if (DCE2_SmbHdrChecks(ssd, smb_hdr) != DCE2_RET__SUCCESS)
+ // If byte count is deemed invalid, alert but continue processing
+ switch (smb_com)
+ {
+ // Interim responses
+ case SMB_COM_TRANSACTION:
+ case SMB_COM_TRANSACTION2:
+ case SMB_COM_NT_TRANSACT:
+ // If word count is 0, byte count must be 0
+ if ((com_info.word_count == 0) && (com_info.smb_type == SMB_TYPE__RESPONSE))
+ {
+ if (smb_bcc != 0)
{
- DebugMessage(DEBUG_DCE_SMB, "Bad SMB header.\n");
-
- *ignore_bytes = sizeof(NbssHdr) + NbssLen((NbssHdr*)data_ptr);
-
- *data_state = DCE2_SMB_DATA_STATE__NETBIOS_HEADER;
-
- dce2_smb_stats.smb_ignored_bytes += *ignore_bytes;
- continue;
+ dce_alert(GID_DCE2, DCE2_SMB_BAD_BCC, (dce2CommonStats*)&dce2_smb_stats);
+ com_info.cmd_error |= DCE2_SMB_COM_ERROR__INVALID_BYTE_COUNT;
}
-
- *data_state = DCE2_SMB_DATA_STATE__NETBIOS_PDU;
+ break;
}
-
- // Fall through
-
- // This state ensures that we have the entire PDU before continuing
- // to process.
- case DCE2_SMB_DATA_STATE__NETBIOS_PDU:
+ // Fall through
+ default:
+ if (!DCE2_SmbIsValidByteCount(smb_com, (uint8_t)com_info.smb_type, smb_bcc))
{
- uint32_t nb_len = NbssLen((NbssHdr*)data_ptr);
- uint32_t data_need = sizeof(NbssHdr) + nb_len;
+ dce_alert(GID_DCE2, DCE2_SMB_BAD_BCC, (dce2CommonStats*)&dce2_smb_stats);
+ com_info.cmd_error |= DCE2_SMB_COM_ERROR__INVALID_BYTE_COUNT;
+ }
+ break;
+ }
- /* It's something we want to inspect so make sure we have the full NBSS packet */
- if (data_len < data_need)
- {
- DebugFormat(DEBUG_DCE_SMB, "Data len(%u) < "
- "NetBIOS SS header + NetBIOS len(%u). "
- "Queueing data.\n", data_len, sizeof(NbssHdr) + nb_len);
+ // Move just past byte count field which is the end of the command
+ DCE2_MOVE(nb_ptr, nb_len, com_info.cmd_size);
- // FIXIT-M add segmentation code
+ // Validate that there is enough data to be able to process the command
+ if (nb_len < DCE2_SmbGetMinByteCount(smb_com, (uint8_t)com_info.smb_type))
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_NB_LT_BCC, (dce2CommonStats*)&dce2_smb_stats);
+ com_info.cmd_error |= DCE2_SMB_COM_ERROR__BAD_LENGTH;
+ }
- return;
- }
+ // The byte count seems to be ignored by Windows and current Samba (3.5.4)
+ // as long as it is less than the amount of data left. If more, an error
+ // is returned.
+ // !!!WARNING!!! the byte count should probably never be used.
+ if (smb_bcc > nb_len)
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_NB_LT_BCC, (dce2CommonStats*)&dce2_smb_stats);
+ // Large byte count doesn't seem to matter for early Samba
+ switch (DCE2_SsnGetPolicy(&ssd->sd))
+ {
+ case DCE2_POLICY__SAMBA_3_0_20:
+ case DCE2_POLICY__SAMBA_3_0_22:
+ case DCE2_POLICY__SAMBA_3_0_37:
+ break;
+ default:
+ com_info.cmd_error |= DCE2_SMB_COM_ERROR__BAD_LENGTH;
+ break;
+ }
+ }
+ else if ((smb_bcc == 0) && (SmbCom(smb_hdr) == SMB_COM_TRANSACTION)
+ && (DCE2_SmbType(ssd) == SMB_TYPE__REQUEST)
+ && (DCE2_SsnGetPolicy(&ssd->sd) == DCE2_POLICY__SAMBA))
+ {
+ // Current Samba errors on a zero byte count Transaction because it
+ // uses it to get the Name string and if zero Name will be NULL and
+ // it won't process it.
+ com_info.cmd_error |= DCE2_SMB_COM_ERROR__BAD_LENGTH;
+ }
- // data_len >= data_need which means data_need <= UINT16_MAX
- // So casts below of data_need to uint16_t are okay.
-
- *data_state = DCE2_SMB_DATA_STATE__NETBIOS_HEADER;
-
- const uint8_t* nb_ptr = data_ptr;
- nb_len = data_need;
- DCE2_MOVE(data_ptr, data_len, (uint16_t)data_need);
-
- switch (ssd->pdu_state)
- {
- case DCE2_SMB_PDU_STATE__COMMAND:
- {
- SmbNtHdr* smb_hdr = (SmbNtHdr*)(nb_ptr + sizeof(NbssHdr));
- DCE2_MOVE(nb_ptr, nb_len, (sizeof(NbssHdr) + sizeof(SmbNtHdr)));
- ssd->cur_rtracker = (rtracker != nullptr)
- ? rtracker : DCE2_SmbFindRequestTracker(ssd, smb_hdr);
- if (ssd->cur_rtracker != nullptr)
- DCE2_SmbProcessCommand(ssd, smb_hdr, nb_ptr, nb_len);
- break;
- }
-
- case DCE2_SMB_PDU_STATE__RAW_DATA:
- //FIXIT-M port raw state
- break;
- default:
- DebugFormat(DEBUG_DCE_SMB, "%s(%d) Invalid SMB PDU "
- "state: %d\n", __FILE__, __LINE__, ssd->pdu_state);
- return;
- }
- break;
- }
-
- default:
- DebugFormat(DEBUG_DCE_SMB, "%s(%d) Invalid SMB Data "
- "state: %d\n", __FILE__, __LINE__, *data_state);
- return;
- }
- }
+ com_info.byte_count = smb_bcc;
}
/********************************************************************
- * Function: DCE2_SmbInitGlobals()
+ * Function: DCE2_SmbProcessCommand()
*
* Purpose:
- * Initializes global variables for SMB processing.
- * Sets up the functions and valid word and byte counts for SMB
- * commands.
- * Sets up AndX chain mappings and valid command chaining for
- * supported policies.
+ * This is the main function for handling SMB commands and command
+ * chaining.
+ * It does an initial check of the command to determine validity
+ * and gets basic information about the command. Then it calls the
+ * specific command function (setup in DCE2_SmbInitGlobals).
+ * If there is command chaining, it will do the chaining foo to
+ * get to the next command.
*
- * Arguments: None
+ * Arguments:
+ * DCE2_SmbSsnData * - pointer to session data structure
+ * SmbNtHdr * - pointer to the SMB header structure
+ * uint8_t * - current pointer to data, i.e. the command
+ * uint32_t - the remaining length
*
* Returns: None
*
********************************************************************/
-void DCE2_SmbInitGlobals()
+static void DCE2_SmbProcessCommand(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
+ const uint8_t* nb_ptr, uint32_t nb_len)
{
- memset(&smb_wcts, 0, sizeof(smb_wcts));
- memset(&smb_bccs, 0, sizeof(smb_bccs));
+ uint8_t smb_com = SmbCom(smb_hdr);
+ DCE2_Ret status = DCE2_RET__ERROR;
+ bool sess_chain = false;
+ bool tree_chain = false;
+ bool open_chain = false;
+ int num_chained = 0;
- // Sets up the function to call for the command and valid word and byte
- // counts for the command. Ensuring valid word and byte counts is very
- // important to processing the command as it will assume the command is
- // legitimate and can access data that is acutally there. Note that
- // commands with multiple word counts indicate a different command
- // structure, however most, if not all just have an extended version
- // of the structure for which the extended part isn't used. If the
- // extended part of a command structure needs to be used, be sure to
- // check the word count in the command function before accessing data
- // in the extended version of the command structure.
- for (int com = 0; com < SMB_MAX_NUM_COMS; com++)
+ while (nb_len > 0)
{
- switch (com)
- {
- case SMB_COM_OPEN:
- // FIXIT-M port DCE2_SmbOpen. Same for other smb_com_funcs
- // smb_com_funcs[com] = DCE2_SmbOpen;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
-
- smb_deprecated_coms[com] = true;
- smb_unusual_coms[com] = false;
-
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 2);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 7);
-
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 2, UINT16_MAX);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
+ // Break out if command not supported
+ if (smb_com_funcs[smb_com] == nullptr)
break;
- case SMB_COM_CREATE:
- //smb_com_funcs[com] = DCE2_SmbCreate;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = true;
- smb_unusual_coms[com] = false;
+ if (smb_deprecated_coms[smb_com])
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_DEPR_COMMAND_USED, (dce2CommonStats*)&dce2_smb_stats);
+ }
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 3);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 1);
+ if (smb_unusual_coms[smb_com])
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_UNUSUAL_COMMAND_USED, (dce2CommonStats*)&dce2_smb_stats);
+ }
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 2, UINT16_MAX);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
- break;
- case SMB_COM_CLOSE:
- //smb_com_funcs[com] = DCE2_SmbClose;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = false;
- smb_unusual_coms[com] = false;
+ DCE2_SmbComInfo com_info;
+ com_info.smb_type = DCE2_SmbType(ssd);
+ com_info.cmd_error = DCE2_SMB_COM_ERROR__COMMAND_OK;
+ com_info.word_count = 0;
+ com_info.smb_com = smb_com;
+ com_info.cmd_size = 0;
+ com_info.byte_count = 0;
+ DCE2_SmbCheckCommand(ssd, smb_hdr, smb_com, nb_ptr, nb_len, com_info);
+ DebugFormat(DEBUG_DCE_SMB, "Processing command: %s (0x%02X)\n",
+ smb_com_strings[smb_com], smb_com);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 3);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 0);
+ // Note that even if the command shouldn't be processed, some of
+ // the command functions need to know and do cleanup or some other
+ // processing.
+ status = smb_com_funcs[smb_com](ssd, smb_hdr,
+ &com_info, nb_ptr, nb_len);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, 0);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
+ if (status != DCE2_RET__SUCCESS)
break;
- case SMB_COM_RENAME:
- //smb_com_funcs[com] = DCE2_SmbRename;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = false;
- smb_unusual_coms[com] = false;
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 1);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 0);
+ // This command is not chainable
+ SmbAndXCom andx_com = smb_chain_map[smb_com];
+ if (andx_com == SMB_ANDX_COM__NONE)
+ break;
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 4, UINT16_MAX);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
+ /**********************************************************
+ * AndX Chaining
+ **********************************************************/
+ const SmbAndXCommon* andx_ptr = (SmbAndXCommon*)nb_ptr;
+ uint8_t smb_com2 = SmbAndXCom2(andx_ptr);
+ if (smb_com2 == SMB_COM_NO_ANDX_COMMAND)
break;
- case SMB_COM_READ:
- //smb_com_funcs[com] = DCE2_SmbRead;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = true;
- smb_unusual_coms[com] = false;
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 5);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 5);
+ DebugFormat(DEBUG_DCE_SMB, "Chained SMB command: %s\n", smb_com_strings[smb_com2]);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, 0);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 3, UINT16_MAX);
- break;
- case SMB_COM_WRITE:
- //smb_com_funcs[com] = DCE2_SmbWrite;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = true;
- smb_unusual_coms[com] = false;
+ num_chained++;
+ if (DCE2_ScSmbMaxChain((dce2SmbProtoConf*)ssd->sd.config) &&
+ (num_chained >= DCE2_ScSmbMaxChain((dce2SmbProtoConf*)ssd->sd.config)))
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_EXCESSIVE_CHAINING, (dce2CommonStats*)&dce2_smb_stats);
+ }
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 5);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 1);
+ // Multiple SessionSetupAndX, TreeConnectAndX, OpenAndX and NtCreateAndX
+ // are only allowed by Samba.
+ if (smb_com == SMB_COM_SESSION_SETUP_ANDX)
+ sess_chain = true;
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 3, UINT16_MAX);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
+ // Check for multiple chained SessionSetupAndX
+ if ((smb_com2 == SMB_COM_SESSION_SETUP_ANDX) && sess_chain)
+ {
+ // There is only one place to return a uid.
+ dce_alert(GID_DCE2, DCE2_SMB_MULT_CHAIN_SS, (dce2CommonStats*)&dce2_smb_stats);
+ // FIXIT-L Should we continue processing?
break;
- case SMB_COM_CREATE_NEW:
- // smb_com_funcs[com] = DCE2_SmbCreateNew;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = true;
- smb_unusual_coms[com] = false;
+ }
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 3);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 1);
+ // Check for chained SessionSetupAndX => .? => LogoffAndX
+ if ((smb_com2 == SMB_COM_LOGOFF_ANDX) && sess_chain)
+ {
+ // This essentially deletes the uid created by the login
+ // and doesn't make any sense.
+ dce_alert(GID_DCE2, DCE2_SMB_CHAIN_SS_LOGOFF, (dce2CommonStats*)&dce2_smb_stats);
+ }
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 2, UINT16_MAX);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
+ if (smb_com == SMB_COM_TREE_CONNECT_ANDX)
+ tree_chain = true;
+
+ // Check for multiple chained TreeConnects
+ if (((smb_com2 == SMB_COM_TREE_CONNECT_ANDX)
+ || (smb_com2 == SMB_COM_TREE_CONNECT)) && tree_chain)
+ {
+ // There is only one place to return a tid.
+ dce_alert(GID_DCE2, DCE2_SMB_MULT_CHAIN_TC, (dce2CommonStats*)&dce2_smb_stats);
+ // FIXIT-L Should we continue processing?
break;
- case SMB_COM_LOCK_AND_READ:
- //smb_com_funcs[com] = DCE2_SmbLockAndRead;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = true;
- smb_unusual_coms[com] = false;
+ }
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 5);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 5);
+ // Check for chained TreeConnectAndX => .? => TreeDisconnect
+ if ((smb_com2 == SMB_COM_TREE_DISCONNECT) && tree_chain)
+ {
+ // This essentially deletes the tid created by the tree connect
+ // and doesn't make any sense.
+ dce_alert(GID_DCE2, DCE2_SMB_CHAIN_TC_TDIS, (dce2CommonStats*)&dce2_smb_stats);
+ }
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, 0);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 3, UINT16_MAX);
- break;
- case SMB_COM_WRITE_AND_UNLOCK:
- //smb_com_funcs[com] = DCE2_SmbWriteAndUnlock;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = true;
- smb_unusual_coms[com] = false;
+ if ((smb_com == SMB_COM_OPEN_ANDX) || (smb_com == SMB_COM_NT_CREATE_ANDX))
+ open_chain = true;
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 5);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 1);
+ // Check for chained OpenAndX/NtCreateAndX => .? => Close
+ if ((smb_com2 == SMB_COM_CLOSE) && open_chain)
+ {
+ // This essentially deletes the fid created by the open command
+ // and doesn't make any sense.
+ dce_alert(GID_DCE2, DCE2_SMB_CHAIN_OPEN_CLOSE, (dce2CommonStats*)&dce2_smb_stats);
+ }
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 3, UINT16_MAX);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
+ // Check that policy allows for such chaining
+ DCE2_Policy policy = DCE2_SsnGetServerPolicy(&ssd->sd);
+ if (smb_chain_funcs[policy][andx_com][smb_com2] == nullptr)
break;
- case SMB_COM_READ_RAW:
- //smb_com_funcs[com] = DCE2_SmbReadRaw;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = true;
- smb_unusual_coms[com] = false;
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 8);
- // With optional OffsetHigh
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 10);
+ DCE2_MOVE(nb_ptr, nb_len, DCE2_ComInfoCommandSize(&com_info));
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, 0);
- // Response is raw data, i.e. without SMB
+ // FIXIT-L Need to test out of order chaining
+ const uint8_t* off2_ptr = (uint8_t*)smb_hdr + SmbAndXOff2(andx_ptr);
+ if (DCE2_SmbCheckAndXOffset(off2_ptr, nb_ptr, nb_len) != DCE2_RET__SUCCESS)
break;
- case SMB_COM_WRITE_RAW:
- //smb_com_funcs[com] = DCE2_SmbWriteRaw;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = true;
- smb_unusual_coms[com] = false;
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 12);
- // With optional OffsetHigh
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 14);
- // Interim server response
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 1);
+ DCE2_MOVE(nb_ptr, nb_len, (off2_ptr - nb_ptr));
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
+ // FIXIT Need to test more.
+ switch (smb_com)
+ {
+ case SMB_COM_SESSION_SETUP_ANDX:
+ case SMB_COM_TREE_CONNECT_ANDX:
+ case SMB_COM_OPEN_ANDX:
+ case SMB_COM_NT_CREATE_ANDX:
+ switch (smb_com2)
+ {
+ case SMB_COM_WRITE:
+ case SMB_COM_WRITE_ANDX:
+ case SMB_COM_TRANSACTION:
+ case SMB_COM_READ_ANDX:
+ if (DCE2_SsnFromClient(ssd->sd.wire_pkt) && open_chain)
+ {
+ DCE2_SmbQueueTmpFileTracker(ssd, ssd->cur_rtracker,
+ SmbUid(smb_hdr), SmbTid(smb_hdr));
+ }
+ break;
+ default:
+ break;
+ }
break;
- case SMB_COM_WRITE_COMPLETE:
- // Final server response to SMB_COM_WRITE_RAW
- //smb_com_funcs[com] = DCE2_SmbWriteComplete;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = true;
- smb_unusual_coms[com] = false;
+ default:
+ break;
+ }
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 1);
+ smb_com = smb_com2;
+ }
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
- break;
+ int smb_type = DCE2_SmbType(ssd);
+ if (smb_type == SMB_TYPE__RESPONSE)
+ {
+ switch (smb_com)
+ {
case SMB_COM_TRANSACTION:
- // smb_com_funcs[com] = DCE2_SmbTransaction;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = false;
- smb_unusual_coms[com] = false;
+ case SMB_COM_TRANSACTION2:
+ case SMB_COM_NT_TRANSACT:
+ case SMB_COM_TRANSACTION_SECONDARY:
+ case SMB_COM_TRANSACTION2_SECONDARY:
+ case SMB_COM_NT_TRANSACT_SECONDARY:
+ // This case means there was an error with the initial response
+ // so the tracker isn't yet officially in response mode
+ if (ssd->cur_rtracker->ttracker.smb_type == SMB_TYPE__REQUEST)
+ {
+ // Samba throws out entire transaction and Windows just this request
+ if (DCE2_SsnIsServerSambaPolicy(&ssd->sd) && (status != DCE2_RET__SUCCESS))
+ break;
- // Word count depends on setup count
- //for (i = 14; i < 256; i++)
- // DCE2_SmbSetValidWordCount(com, SMB_TYPE__REQUEST, i);
- // In reality, all subcommands of SMB_COM_TRANSACTION requests
- // have a setup count of 2 words.
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 16);
-
- // \PIPE\LANMAN
- // Not something the preprocessor is looking at as it
- // doesn't carry DCE/RPC but don't want to false positive
- // on the preprocessor event.
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 14);
-
- // Word count depends on setup count
- //for (i = 10; i < 256; i++)
- // DCE2_SmbSetValidWordCount(com, SMB_TYPE__RESPONSE, i);
- // In reality, all subcommands of SMB_COM_TRANSACTION responses
- // have a setup count of 0 words.
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 10);
-
- // Interim server response
- // When client sends an incomplete transaction and needs to
- // send TransactionSecondary requests to complete request.
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 0);
-
- // Exception will be made for Interim responses when
- // byte count is checked.
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX);
- break;
- case SMB_COM_TRANSACTION_SECONDARY:
- //smb_com_funcs[com] = DCE2_SmbTransactionSecondary;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = false;
- smb_unusual_coms[com] = false;
-
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 8);
- // Response is an SMB_COM_TRANSACTION
-
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX);
- break;
- case SMB_COM_WRITE_AND_CLOSE:
- // smb_com_funcs[com] = DCE2_SmbWriteAndClose;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = true;
- smb_unusual_coms[com] = false;
-
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 6);
- // For some reason MS-CIFS specifies a version of this command
- // with 6 extra words (12 bytes) of reserved, i.e. useless data.
- // Maybe had intentions of extending and defining the data at
- // some point, but there is no documentation that I could find
- // that does.
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 12);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 1);
-
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 1, UINT16_MAX);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
- break;
- case SMB_COM_OPEN_ANDX:
- //smb_com_funcs[com] = DCE2_SmbOpenAndX;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = true;
- smb_unusual_coms[com] = false;
-
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 15);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 15);
- // Extended response
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 19);
-
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 2, UINT16_MAX);
- // MS-SMB says that Windows 2000, XP and Vista set this to
- // some arbitrary value that is ignored on receipt.
- //DCE2_SmbSetValidByteCount(com, SMB_TYPE__RESPONSE, 0, 0);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX);
- break;
- case SMB_COM_READ_ANDX:
- // smb_com_funcs[com] = DCE2_SmbReadAndX;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = false;
- smb_unusual_coms[com] = false;
-
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 10);
- // With optional OffsetHigh
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 12);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 12);
-
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, 0);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX);
+ if (!DCE2_SmbIsTransactionComplete(&ssd->cur_rtracker->ttracker))
+ return;
+ }
+ else
+ {
+ if ((status == DCE2_RET__SUCCESS)
+ && !DCE2_SmbIsTransactionComplete(&ssd->cur_rtracker->ttracker))
+ return;
+ }
break;
- case SMB_COM_WRITE_ANDX:
- // smb_com_funcs[com] = DCE2_SmbWriteAndX;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = false;
- smb_unusual_coms[com] = false;
-
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 12);
- // With optional OffsetHigh
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 14);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 6);
-
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 1, UINT16_MAX);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
+ case SMB_COM_WRITE_RAW:
+ if ((status == DCE2_RET__SUCCESS)
+ && (ssd->cur_rtracker->writeraw_remaining != 0))
+ return;
break;
- case SMB_COM_TRANSACTION2:
- //smb_com_funcs[com] = DCE2_SmbTransaction2;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = false;
- smb_unusual_coms[com] = false;
-
- // Word count depends on setup count
- //for (i = 14; i < 256; i++)
- // DCE2_SmbSetValidWordCount(com, SMB_TYPE__REQUEST, i);
- // In reality, all subcommands of SMB_COM_TRANSACTION2
- // requests have a setup count of 1 word.
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 15);
-
- // Word count depends on setup count
- //for (i = 10; i < 256; i++)
- // DCE2_SmbSetValidWordCount(com, SMB_TYPE__RESPONSE, i);
- // In reality, all subcommands of SMB_COM_TRANSACTION2
- // responses have a setup count of 0 or 1 word.
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 10);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 11);
-
- // Interim server response
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 0);
-
- // Exception will be made for Interim responses when
- // byte count is checked.
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX);
+ default:
break;
- case SMB_COM_TRANSACTION2_SECONDARY:
- //smb_com_funcs[com] = DCE2_SmbTransaction2Secondary;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = false;
- smb_unusual_coms[com] = false;
+ }
+ }
+ else if (status != DCE2_RET__IGNORE)
+ {
+ switch (smb_com)
+ {
+ case SMB_COM_TRANSACTION:
+ case SMB_COM_TRANSACTION_SECONDARY:
+ if (DCE2_SsnIsWindowsPolicy(&ssd->sd))
+ {
+ if (!ssd->cur_rtracker->ttracker.one_way
+ || !DCE2_SmbIsTransactionComplete(&ssd->cur_rtracker->ttracker))
+ return;
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 9);
- // Response is an SMB_COM_TRANSACTION2
+ // Remove the request tracker if transaction is one-way and
+ // all data and parameters have been sent
+ break;
+ }
+ default:
+ // Anything else, keep the request tracker
+ return;
+ }
+ }
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX);
- break;
- case SMB_COM_TREE_CONNECT:
- //smb_com_funcs[com] = DCE2_SmbTreeConnect;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = true;
- smb_unusual_coms[com] = false;
+ DCE2_SmbRemoveRequestTracker(ssd, ssd->cur_rtracker);
+ ssd->cur_rtracker = nullptr;
+}
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 0);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 2);
+/********************************************************************
+ * Function: DCE2_SmbInspect()
+ *
+ * Purpose:
+ * Determines whether the SMB command is something the preprocessor
+ * needs to inspect.
+ * This function returns a DCE2_SmbRequestTracker which tracks command
+ * requests / responses.
+ *
+ * Arguments:
+ * DCE2_SmbSsnData * - the session data structure.
+ * const SmbNtHdr * - pointer to the SMB header.
+ *
+ * Returns:
+ * DCE2_SmbRequestTracker * - nullptr if it's not something we want to or can
+ * inspect.
+ * Otherwise an initialized structure if request
+ * and the found structure if response.
+ *
+ ********************************************************************/
+static DCE2_SmbRequestTracker* DCE2_SmbInspect(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr)
+{
+ int smb_com = SmbCom(smb_hdr);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 6, UINT16_MAX);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
- break;
- case SMB_COM_TREE_DISCONNECT:
- //smb_com_funcs[com] = DCE2_SmbTreeDisconnect;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = false;
- smb_unusual_coms[com] = false;
+ DebugFormat(DEBUG_DCE_SMB, "SMB command: %s (0x%02X)\n",
+ smb_com_strings[smb_com], smb_com);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 0);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 0);
+ if (smb_com_funcs[smb_com] == nullptr)
+ {
+ DebugMessage(DEBUG_DCE_SMB, "Command isn't processed "
+ "by preprocessor.\n");
+ return nullptr;
+ }
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, 0);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
- break;
+ // See if this is something we need to inspect
+ DCE2_Policy policy = DCE2_SsnGetServerPolicy(&ssd->sd);
+ DCE2_SmbRequestTracker* rtracker = nullptr;
+ if (DCE2_SmbType(ssd) == SMB_TYPE__REQUEST)
+ {
+ switch (smb_com)
+ {
case SMB_COM_NEGOTIATE:
- // Not doing anything with this command right now.
- // smb_com_funcs[com] = DCE2_SmbNegotiate;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = false;
- smb_unusual_coms[com] = false;
-
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 0);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 1);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 13);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 17);
-
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 2, UINT16_MAX);
- // This can vary depending on dialect so just set wide.
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX);
+ if (ssd->ssn_state_flags & DCE2_SMB_SSN_STATE__NEGOTIATED)
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_MULTIPLE_NEGOTIATIONS,
+ (dce2CommonStats*)&dce2_smb_stats);
+ return nullptr;
+ }
break;
case SMB_COM_SESSION_SETUP_ANDX:
- //smb_com_funcs[com] = DCE2_SmbSessionSetupAndX;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = false;
- smb_unusual_coms[com] = false;
-
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 10);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 12);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 13);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 3);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 4);
-
- // These can vary so just set wide.
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX);
break;
+ case SMB_COM_TREE_CONNECT:
+ case SMB_COM_TREE_CONNECT_ANDX:
+ case SMB_COM_RENAME:
case SMB_COM_LOGOFF_ANDX:
- //smb_com_funcs[com] = DCE2_SmbLogoffAndX;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = false;
- smb_unusual_coms[com] = false;
-
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 2);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 2);
- // Windows responds to a LogoffAndX => SessionSetupAndX with just a
- // LogoffAndX and with the word count field containing 3, but only
- // has 2 words
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 3);
-
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, 0);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
+ if (DCE2_SmbFindUid(ssd, SmbUid(smb_hdr)) != DCE2_RET__SUCCESS)
+ return nullptr;
break;
- case SMB_COM_TREE_CONNECT_ANDX:
- //smb_com_funcs[com] = DCE2_SmbTreeConnectAndX;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = false;
- smb_unusual_coms[com] = false;
+ default:
+ if (DCE2_SmbFindTid(ssd, SmbTid(smb_hdr)) != DCE2_RET__SUCCESS)
+ {
+ DebugFormat(DEBUG_DCE_SMB,
+ "Couldn't find Tid (%u)\n", SmbTid(smb_hdr));
+ return nullptr;
+ }
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 4);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 2);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 3);
- // Extended response
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 7);
+ if (DCE2_SmbIsTidIPC(ssd, SmbTid(smb_hdr)))
+ {
+ switch (smb_com)
+ {
+ case SMB_COM_OPEN:
+ case SMB_COM_CREATE:
+ case SMB_COM_CREATE_NEW:
+ case SMB_COM_WRITE_AND_CLOSE:
+ case SMB_COM_WRITE_AND_UNLOCK:
+ case SMB_COM_READ:
+ // Samba doesn't allow these commands under an IPC tree
+ switch (policy)
+ {
+ case DCE2_POLICY__SAMBA:
+ case DCE2_POLICY__SAMBA_3_0_37:
+ case DCE2_POLICY__SAMBA_3_0_22:
+ case DCE2_POLICY__SAMBA_3_0_20:
+ DebugMessage(DEBUG_DCE_SMB, "Samba doesn't "
+ "process this command under an IPC tree.\n");
+ return nullptr;
+ default:
+ break;
+ }
+ break;
+ case SMB_COM_READ_RAW:
+ case SMB_COM_WRITE_RAW:
+ // Samba and Windows Vista on don't allow these commands
+ // under an IPC tree, whether or not the raw read/write
+ // flag is set in the Negotiate capabilities.
+ // Windows RSTs the connection and Samba FINs it.
+ switch (policy)
+ {
+ case DCE2_POLICY__WINVISTA:
+ case DCE2_POLICY__WIN2008:
+ case DCE2_POLICY__WIN7:
+ case DCE2_POLICY__SAMBA:
+ case DCE2_POLICY__SAMBA_3_0_37:
+ case DCE2_POLICY__SAMBA_3_0_22:
+ case DCE2_POLICY__SAMBA_3_0_20:
+ DebugMessage(DEBUG_DCE_SMB, "Samba and "
+ "Windows Vista on don't process this "
+ "command under an IPC tree.\n");
+ return nullptr;
+ default:
+ break;
+ }
+ break;
+ case SMB_COM_LOCK_AND_READ:
+ // The lock will fail so the read won't happen
+ return nullptr;
+ default:
+ break;
+ }
+ }
+ else // Not IPC
+ {
+ switch (smb_com)
+ {
+ // These commands are only used for IPC
+ case SMB_COM_TRANSACTION:
+ case SMB_COM_TRANSACTION_SECONDARY:
+ DebugMessage(DEBUG_DCE_SMB, "secondary transaction not IPC.\n");
+ return nullptr;
+ case SMB_COM_READ_RAW:
+ case SMB_COM_WRITE_RAW:
+ // Windows Vista on don't seem to support these
+ // commands, whether or not the raw read/write
+ // flag is set in the Negotiate capabilities.
+ // Windows RSTs the connection.
+ switch (policy)
+ {
+ case DCE2_POLICY__WINVISTA:
+ case DCE2_POLICY__WIN2008:
+ case DCE2_POLICY__WIN7:
+ DebugMessage(DEBUG_DCE_SMB,
+ "Windows Vista on don't process "
+ "this command.\n");
+ return nullptr;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ }
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 3, UINT16_MAX);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 2, UINT16_MAX);
+ switch (smb_com)
+ {
+ case SMB_COM_TRANSACTION_SECONDARY:
+ case SMB_COM_TRANSACTION2_SECONDARY:
+ case SMB_COM_NT_TRANSACT_SECONDARY:
+ rtracker = DCE2_SmbFindRequestTracker(ssd, smb_hdr);
break;
+ case SMB_COM_TRANSACTION:
+ case SMB_COM_TRANSACTION2:
case SMB_COM_NT_TRANSACT:
- //smb_com_funcs[com] = DCE2_SmbNtTransact;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = false;
- smb_unusual_coms[com] = false;
+ // If there is already and existing request tracker
+ // and the transaction is not complete, server will
+ // return an error.
+ rtracker = DCE2_SmbFindRequestTracker(ssd, smb_hdr);
+ if (rtracker != nullptr)
+ break;
+ // Fall through
+ default:
+ rtracker = DCE2_SmbNewRequestTracker(ssd, smb_hdr);
+ break;
+ }
+ }
+ else
+ {
+ rtracker = DCE2_SmbFindRequestTracker(ssd, smb_hdr);
+ }
- // Word count depends on setup count
- // In reality, all subcommands of SMB_COM_NT_TRANSACT
- // requests have a setup count of 0 or 4 words.
- //for (i = 19; i < 256; i++)
- // DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, i);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 19);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 23);
+ return rtracker;
+}
- // Word count depends on setup count
- // In reality, all subcommands of SMB_COM_NT_TRANSACT
- // responses have a setup count of 0 or 1 word.
- //for (i = 18; i < 256; i++)
- // DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, i);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 18);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 19);
+/********************************************************************
+ * Function: DCE2_SmbCheckData()
+ *
+ * Purpose:
+ * Ensures that the data size reported in an SMB command is kosher.
+ *
+ * Arguments:
+ * DCE2_SmbSsnData * - SMB session data structure
+ * const uint8_t * - pointer to start of SMB header where offset is
+ * taken from.
+ * const uint8_t * - current pointer - should be right after command
+ * structure.
+ * const uint32_t - remaining data left in PDU from current pointer.
+ * const uint16_t - the byte count from the SMB command
+ * const uint16_t - reported data count in SMB command
+ * const uint16_t - reported data offset in SMB command
+ *
+ * Returns:
+ * DCE2_Ret - DCE2_RET__ERROR if data should not be processed
+ * DCE2_RET__SUCCESS if data can be processed
+ *
+ ********************************************************************/
+static DCE2_Ret DCE2_SmbCheckData(DCE2_SmbSsnData*,
+ const uint8_t* smb_hdr_ptr, const uint8_t* nb_ptr,
+ const uint32_t nb_len, const uint16_t bcc,
+ const uint32_t dcnt, uint16_t doff)
+{
+ const uint8_t* offset = smb_hdr_ptr + doff;
+ const uint8_t* nb_end = nb_ptr + nb_len;
- // Interim server response
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 0);
+ // Byte counts don't usually matter, so no error but still alert
+ // Don't alert in the case where the data count is larger than what the
+ // byte count can handle. This can happen if CAP_LARGE_READX or
+ // CAP_LARGE_WRITEX were negotiated.
+ if ((dcnt <= UINT16_MAX) && (bcc < dcnt))
+ dce_alert(GID_DCE2, DCE2_SMB_BCC_LT_DSIZE, (dce2CommonStats*)&dce2_smb_stats);
- // Exception will be made for Interim responses when
- // byte count is checked.
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX);
- break;
- case SMB_COM_NT_TRANSACT_SECONDARY:
- //smb_com_funcs[com] = DCE2_SmbNtTransactSecondary;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = false;
- smb_unusual_coms[com] = false;
+ if (offset > nb_end)
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_BAD_OFF, (dce2CommonStats*)&dce2_smb_stats);
+ // Error if offset is beyond data left
+ return DCE2_RET__ERROR;
+ }
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 18);
- // Response is an SMB_COM_NT_TRANSACT
+ // Only check if the data count is non-zero
+ if ((dcnt != 0) && (offset < nb_ptr))
+ {
+ // Not necessarily and error if the offset puts the data
+ // before or in the command structure.
+ dce_alert(GID_DCE2, DCE2_SMB_BAD_OFF, (dce2CommonStats*)&dce2_smb_stats);
+ }
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX);
+ // Not necessarily an error if the addition of the data count goes
+ // beyond the data left
+ if (((offset + dcnt) > nb_end) // beyond data left
+ || ((offset + dcnt) < offset)) // wrap
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_NB_LT_DSIZE, (dce2CommonStats*)&dce2_smb_stats);
+ }
+
+ return DCE2_RET__SUCCESS;
+}
+
+// Temporary command function placeholder, until all of them are ported
+DCE2_Ret DCE2_SmbComFuncPlaceholder(DCE2_SmbSsnData*, const SmbNtHdr*,
+ const DCE2_SmbComInfo*, const uint8_t*, uint32_t)
+{
+ return DCE2_RET__SUCCESS;
+}
+
+static bool DCE2_SmbAutodetect(Packet* p)
+{
+ if (p->dsize > (sizeof(NbssHdr) + sizeof(SmbNtHdr)))
+ {
+ NbssHdr* nb_hdr = (NbssHdr*)p->data;
+ SmbNtHdr* smb_hdr = (SmbNtHdr*)(p->data + sizeof(NbssHdr));
+
+ if ((SmbId(smb_hdr) != DCE2_SMB_ID)
+ && (SmbId(smb_hdr) != DCE2_SMB2_ID))
+ {
+ return false;
+ }
+
+ switch (NbssType(nb_hdr))
+ {
+ // FIXIT-L currently all ports are treated as autodetect.
+ // On port 139, there is always an initial Session Request / Session Positive/Negative
+ // response.
+ // These message types were added , to make sure port 139 is treated as smb.
+ // Remove once detect/autodetect supported.
+ case NBSS_SESSION_TYPE__REQUEST:
+ if (DCE2_SsnFromClient(p))
+ return true;
break;
- case SMB_COM_NT_CREATE_ANDX:
- //smb_com_funcs[com] = DCE2_SmbNtCreateAndX;
- smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
- smb_deprecated_coms[com] = false;
- smb_unusual_coms[com] = false;
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 24);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 34);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 26);
- // Extended response - though there are actually 50 words
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 42);
+ case NBSS_SESSION_TYPE__POS_RESPONSE:
+ case NBSS_SESSION_TYPE__NEG_RESPONSE:
+ if (DCE2_SsnFromServer(p))
+ return true;
+ break;
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 2, UINT16_MAX);
- // MS-SMB indicates that this field should be 0 but may be
- // sent uninitialized so basically ignore it.
- //DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX);
+ case NBSS_SESSION_TYPE__MESSAGE:
+ return true;
break;
default:
- smb_com_funcs[com] = nullptr;
- smb_deprecated_coms[com] = false;
- smb_unusual_coms[com] = false;
- // Just set to all valid since the specific command won't
- // be processed. Don't want to false positive on these.
- for (int i = 0; i < 256; i++)
- {
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, (uint8_t)i);
- DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, (uint8_t)i);
- }
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX);
- DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX);
break;
}
}
- // Maps commands for use in quickly determining if a command
- // is chainable and what command it is.
- for (int com = 0; com < SMB_MAX_NUM_COMS; com++)
- {
- switch (com)
- {
- case SMB_COM_SESSION_SETUP_ANDX:
- smb_chain_map[com] = SMB_ANDX_COM__SESSION_SETUP_ANDX;
- break;
- case SMB_COM_LOGOFF_ANDX:
- smb_chain_map[com] = SMB_ANDX_COM__LOGOFF_ANDX;
- break;
- case SMB_COM_TREE_CONNECT_ANDX:
- smb_chain_map[com] = SMB_ANDX_COM__TREE_CONNECT_ANDX;
- break;
- case SMB_COM_OPEN_ANDX:
- smb_chain_map[com] = SMB_ANDX_COM__OPEN_ANDX;
- break;
- case SMB_COM_NT_CREATE_ANDX:
- smb_chain_map[com] = SMB_ANDX_COM__NT_CREATE_ANDX;
- break;
- case SMB_COM_WRITE_ANDX:
- smb_chain_map[com] = SMB_ANDX_COM__WRITE_ANDX;
- break;
- case SMB_COM_READ_ANDX:
- smb_chain_map[com] = SMB_ANDX_COM__READ_ANDX;
- break;
- default:
- smb_chain_map[com] = SMB_ANDX_COM__NONE;
- break;
+ return false;
+}
+
+void DCE2_SmbDataFree(DCE2_SmbSsnData* ssd)
+{
+ if (ssd == nullptr)
+ return;
+
+ // FIXIT This tries to account for the situation where we never knew the file
+ // size and the TCP session was shutdown before an SMB_COM_CLOSE on the file.
+ // Possibly need to add callback to fileAPI since it may have already
+ // released it's resources.
+ //DCE2_SmbFinishFileAPI(ssd);
+
+ if (ssd->uids != nullptr)
+ {
+ DCE2_ListDestroy(ssd->uids);
+ ssd->uids = nullptr;
+ }
+
+ if (ssd->tids != nullptr)
+ {
+ DCE2_ListDestroy(ssd->tids);
+ ssd->tids = nullptr;
+ }
+
+ DCE2_SmbCleanFileTracker(&ssd->ftracker);
+ if (ssd->ftrackers != nullptr)
+ {
+ DCE2_ListDestroy(ssd->ftrackers);
+ ssd->ftrackers = nullptr;
+ }
+
+ DCE2_SmbCleanRequestTracker(&ssd->rtracker);
+ if (ssd->rtrackers != nullptr)
+ {
+ DCE2_QueueDestroy(ssd->rtrackers);
+ ssd->rtrackers = nullptr;
+ }
+
+ if (ssd->cli_seg != nullptr)
+ {
+ DCE2_BufferDestroy(ssd->cli_seg);
+ ssd->cli_seg = nullptr;
+ }
+
+ if (ssd->srv_seg != nullptr)
+ {
+ DCE2_BufferDestroy(ssd->srv_seg);
+ ssd->srv_seg = nullptr;
+ }
+}
+
+Dce2SmbFlowData::Dce2SmbFlowData() : FlowData(flow_id)
+{
+}
+
+Dce2SmbFlowData::~Dce2SmbFlowData()
+{
+ DCE2_SmbDataFree(&dce2_smb_session);
+}
+
+unsigned Dce2SmbFlowData::flow_id = 0;
+
+DCE2_SmbSsnData* get_dce2_smb_session_data(Flow* flow)
+{
+ Dce2SmbFlowData* fd = (Dce2SmbFlowData*)flow->get_application_data(
+ Dce2SmbFlowData::flow_id);
+
+ return fd ? &fd->dce2_smb_session : nullptr;
+}
+
+static DCE2_SmbSsnData* set_new_dce2_smb_session(Packet* p)
+{
+ Dce2SmbFlowData* fd = new Dce2SmbFlowData;
+
+ memset(&fd->dce2_smb_session,0,sizeof(DCE2_SmbSsnData));
+ p->flow->set_application_data(fd);
+ return(&fd->dce2_smb_session);
+}
+
+static DCE2_SmbSsnData* dce2_create_new_smb_session(Packet* p, dce2SmbProtoConf* config)
+{
+ DCE2_SmbSsnData* dce2_smb_sess = nullptr;
+ Profile profile(dce2_smb_pstat_new_session);
+
+ //FIXIT-M Re-evaluate after infrastructure/binder support if autodetect here
+ //is necessary
+
+ if (DCE2_SmbAutodetect(p))
+ {
+ DebugMessage(DEBUG_DCE_SMB, "DCE over SMB packet detected\n");
+ DebugMessage(DEBUG_DCE_SMB, "Creating new session\n");
+
+ 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 = 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.ropts);
+
+ dce2_smb_stats.smb_sessions++;
+ DebugFormat(DEBUG_DCE_SMB,"Created (%p)\n", (void*)dce2_smb_sess);
+
+ 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.wire_pkt = p;
+ dce2_smb_sess->sd.config = (void*)config;
+
+ DCE2_SsnSetAutodetected(&dce2_smb_sess->sd, p);
}
}
- // Sets up the valid command chaining combinations per policy
- for (int policy = 0; policy < DCE2_POLICY__MAX; policy++)
+ return dce2_smb_sess;
+}
+
+static DCE2_SmbSsnData* dce2_handle_smb_session(Packet* p, dce2SmbProtoConf* config)
+{
+ Profile profile(dce2_smb_pstat_session);
+
+ DCE2_SmbSsnData* dce2_smb_sess = get_dce2_smb_session_data(p->flow);
+
+ if (dce2_smb_sess == nullptr)
{
- for (int andx = SMB_ANDX_COM__NONE; andx < SMB_ANDX_COM__MAX; andx++)
+ dce2_smb_sess = dce2_create_new_smb_session(p, config);
+ }
+ else
+ {
+ DCE2_SsnData* sd = (DCE2_SsnData*)dce2_smb_sess;
+ sd->wire_pkt = p;
+
+ if (DCE2_SsnAutodetected(sd) && !(p->packet_flags & sd->autodetect_dir))
{
- /* com is the chained command or com2 */
- for (int com = 0; com < SMB_MAX_NUM_COMS; com++)
+ /* Try to autodetect in opposite direction */
+ if (!DCE2_SmbAutodetect(p))
{
- DCE2_SmbComFunc com_func = nullptr;
+ DebugMessage(DEBUG_DCE_SMB, "Bad autodetect.\n");
+ DCE2_SsnNoInspect(sd);
+ dce2_smb_stats.sessions_aborted++;
+ dce2_smb_stats.bad_autodetects++;
+ return nullptr;
+ }
+ DCE2_SsnClearAutodetected(sd);
+ }
+ }
+ DebugFormat(DEBUG_DCE_SMB, "Session pointer: %p\n", (void*)dce2_smb_sess);
- switch (policy)
- {
- case DCE2_POLICY__WIN2000:
- case DCE2_POLICY__WINXP:
- case DCE2_POLICY__WINVISTA:
- case DCE2_POLICY__WIN2003:
- case DCE2_POLICY__WIN2008:
- case DCE2_POLICY__WIN7:
- switch (andx)
- {
- case SMB_ANDX_COM__SESSION_SETUP_ANDX:
- switch (com)
- {
- case SMB_COM_TREE_CONNECT_ANDX:
- case SMB_COM_OPEN:
- case SMB_COM_OPEN_ANDX:
- case SMB_COM_CREATE:
- case SMB_COM_CREATE_NEW:
- com_func = smb_com_funcs[com];
- break;
- case SMB_COM_TRANSACTION:
- if (policy == DCE2_POLICY__WIN2000)
- com_func = smb_com_funcs[com];
- break;
- default:
- break;
- }
- break;
- case SMB_ANDX_COM__LOGOFF_ANDX:
- switch (com)
- {
- case SMB_COM_SESSION_SETUP_ANDX:
- case SMB_COM_TREE_CONNECT_ANDX: // Only for responses
- com_func = smb_com_funcs[com];
- break;
- default:
- break;
- }
- break;
- case SMB_ANDX_COM__TREE_CONNECT_ANDX:
- switch (com)
- {
- case SMB_COM_OPEN:
- case SMB_COM_CREATE:
- case SMB_COM_CREATE_NEW:
- com_func = smb_com_funcs[com];
- break;
- case SMB_COM_TRANSACTION:
- if (policy == DCE2_POLICY__WIN2000)
- com_func = smb_com_funcs[com];
- break;
- default:
- break;
- }
- break;
- case SMB_ANDX_COM__OPEN_ANDX:
- break;
- case SMB_ANDX_COM__NT_CREATE_ANDX:
- switch (com)
- {
- case SMB_COM_READ_ANDX: // Only for normal files
- com_func = smb_com_funcs[com];
- break;
- default:
- break;
- }
- break;
- case SMB_ANDX_COM__WRITE_ANDX:
- switch (com)
- {
- case SMB_COM_CLOSE:
- case SMB_COM_WRITE_ANDX:
- case SMB_COM_READ:
- case SMB_COM_READ_ANDX:
- com_func = smb_com_funcs[com];
- break;
- default:
+ // FIXIT-M add remaining session handling logic
+
+ return dce2_smb_sess;
+}
+
+/********************************************************************
+ * Function: DCE2_NbssHdrChecks()
+ *
+ * Purpose:
+ * Does validation of the NetBIOS header. SMB will only run over
+ * the Session Message type. On port 139, there is always an
+ * initial Session Request / Session Positive/Negative response
+ * followed by the normal SMB conversation, i.e. Negotiate,
+ * SessionSetupAndX, etc.
+ * Side effects are potential alerts for anomolous behavior.
+ *
+ * Arguments:
+ * DCE2_SmbSsnData * - the session data structure.
+ * const NbssHdr * - pointer to the NetBIOS Session Service
+ * header structure. Size is already validated.
+ *
+ * Returns:
+ * DCE2_Ret - DCE2_RET__SUCCESS if all goes well and processing
+ * should continue.
+ * DCE2_RET__IGNORE if it's not something we need to
+ * look at.
+ * DCE2_RET__ERROR if an invalid NetBIOS Session
+ * Service type is found.
+ *
+ ********************************************************************/
+static DCE2_Ret DCE2_NbssHdrChecks(DCE2_SmbSsnData* ssd, const NbssHdr* nb_hdr)
+{
+ Packet* p = ssd->sd.wire_pkt;
+
+ DebugMessage(DEBUG_DCE_SMB, "NetBIOS Session Service type: ");
+
+ switch (NbssType(nb_hdr))
+ {
+ case NBSS_SESSION_TYPE__MESSAGE:
+ /* Only want to look at session messages */
+ DebugMessage(DEBUG_DCE_SMB, "Session Message\n");
+
+ if (!DCE2_SmbIsRawData(ssd))
+ {
+ uint32_t nb_len = NbssLen(nb_hdr);
+
+ if (nb_len == 0)
+ return DCE2_RET__IGNORE;
+
+ if (nb_len < sizeof(SmbNtHdr))
+ {
+ DebugFormat(DEBUG_DCE_SMB, "NetBIOS SS len(%u) < SMB header len(%u).\n",
+ sizeof(SmbNtHdr), sizeof(NbssHdr) + nb_len);
+
+ // FIXIT-M port segment check
+ // Same for all cases below
+ dce_alert(GID_DCE2, DCE2_SMB_NB_LT_SMBHDR, (dce2CommonStats*)&dce2_smb_stats);
+ return DCE2_RET__IGNORE;
+ }
+ }
+
+ return DCE2_RET__SUCCESS;
+
+ case NBSS_SESSION_TYPE__REQUEST:
+ DebugMessage(DEBUG_DCE_SMB, "Session Request\n");
+ if (DCE2_SsnFromServer(p))
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_BAD_NBSS_TYPE, (dce2CommonStats*)&dce2_smb_stats);
+ }
+
+ break;
+
+ case NBSS_SESSION_TYPE__POS_RESPONSE:
+ case NBSS_SESSION_TYPE__NEG_RESPONSE:
+ case NBSS_SESSION_TYPE__RETARGET_RESPONSE:
+ if (DCE2_SsnFromClient(p))
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_BAD_NBSS_TYPE, (dce2CommonStats*)&dce2_smb_stats);
+ }
+
+ break;
+
+ case NBSS_SESSION_TYPE__KEEP_ALIVE:
+ DebugMessage(DEBUG_DCE_SMB, "Session Keep Alive\n");
+ break;
+
+ default:
+ DebugFormat(DEBUG_DCE_SMB,
+ "Invalid Session Service type: 0x%02X\n", NbssType(nb_hdr));
+ dce_alert(GID_DCE2, DCE2_SMB_BAD_NBSS_TYPE, (dce2CommonStats*)&dce2_smb_stats);
+
+ return DCE2_RET__ERROR;
+ }
+
+ return DCE2_RET__IGNORE;
+}
+
+/********************************************************************
+ * Function: DCE2_SmbProcess()
+ *
+ * Purpose:
+ * This is the main entry point for SMB processing.
+ *
+ * Arguments:
+ * DCE2_SmbSsnData * - the session data structure.
+ *
+ * Returns: None
+ *
+ ********************************************************************/
+void DCE2_SmbProcess(DCE2_SmbSsnData* ssd)
+{
+ DebugMessage(DEBUG_DCE_SMB, "Processing SMB packet.\n");
+ dce2_smb_stats.smb_pkts++;
+
+ const Packet* p = ssd->sd.wire_pkt;
+ const uint8_t* data_ptr = p->data;
+ uint16_t data_len = p->dsize;
+
+ /* Have to account for segmentation. Even though stream will give
+ * us larger chunks, we might end up in the middle of something */
+ while (data_len > 0)
+ {
+ // We are ignoring an entire PDU or junk data so state should be NETBIOS_HEADER
+ // Note that it could be TCP segmented so ignore_bytes could be greater than
+ // the amount of data we have
+ uint32_t* ignore_bytes = DCE2_SmbGetIgnorePtr(ssd);
+ if (*ignore_bytes)
+ {
+ DebugFormat(DEBUG_DCE_SMB, "Ignoring %u bytes\n", *ignore_bytes);
+
+ if (data_len <= *ignore_bytes)
+ {
+ *ignore_bytes -= data_len;
+ return;
+ }
+ else
+ {
+ /* ignore bytes is less than UINT16_MAX */
+ DCE2_MOVE(data_ptr, data_len, (uint16_t)*ignore_bytes);
+ *ignore_bytes = 0;
+ }
+ }
+
+ DCE2_SmbDataState* data_state = DCE2_SmbGetDataState(ssd);
+ DCE2_SmbRequestTracker* rtracker = nullptr;
+ switch (*data_state)
+ {
+ // This state is to verify it's a NetBIOS Session Message packet
+ // and to get the length of the SMB PDU. Also does the SMB junk
+ // data check. If it's not a Session Message the data isn't
+ // processed since it won't be carrying SMB.
+ case DCE2_SMB_DATA_STATE__NETBIOS_HEADER:
+ {
+ uint32_t data_need = sizeof(NbssHdr);
+
+ // See if there is enough data to process the NetBIOS header
+ if (data_len < data_need)
+ {
+ DebugFormat(DEBUG_DCE_SMB, "Data len(%u) < NetBIOS SS header(%u). "
+ "Queueing data.\n", data_len, data_need);
+
+ // FIXIT-M port segmentation code
+ return;
+ }
+
+ // Set the NetBIOS header structure
+ NbssHdr* nb_hdr = (NbssHdr*)data_ptr;
+ uint32_t nb_len = NbssLen(nb_hdr);
+
+ DebugFormat(DEBUG_DCE_SMB, "NetBIOS PDU length: %u\n", nb_len);
+
+ DCE2_Ret status = DCE2_NbssHdrChecks(ssd, nb_hdr);
+ if (status != DCE2_RET__SUCCESS)
+ {
+ DebugMessage(DEBUG_DCE_SMB, "Not a NetBIOS Session Message.\n");
+
+ if (status == DCE2_RET__IGNORE)
+ {
+ DebugMessage(DEBUG_DCE_SMB, "Valid NetBIOS header "
+ "type so ignoring NetBIOS length bytes.\n");
+ *ignore_bytes = data_need + nb_len;
+ }
+ else // nb_ret == DCE2_RET__ERROR, i.e. invalid NetBIOS type
+ {
+ DebugMessage(DEBUG_DCE_SMB, "Not a valid NetBIOS "
+ "header type so trying to find \\xffSMB to "
+ "determine how many bytes to ignore.\n");
+ *ignore_bytes = DCE2_IgnoreJunkData(data_ptr, data_len, data_need + nb_len);
+ }
+
+ dce2_smb_stats.smb_ignored_bytes += *ignore_bytes;
+ continue;
+ }
+
+ switch (ssd->pdu_state)
+ {
+ case DCE2_SMB_PDU_STATE__COMMAND:
+ *data_state = DCE2_SMB_DATA_STATE__SMB_HEADER;
+ break;
+ case DCE2_SMB_PDU_STATE__RAW_DATA:
+ *data_state = DCE2_SMB_DATA_STATE__NETBIOS_PDU;
+ // Continue here because of fall through below
+ continue;
+ default:
+ DebugFormat(DEBUG_DCE_SMB,"%s(%d) Invalid SMB PDU "
+ "state: %d\n", __FILE__, __LINE__, ssd->pdu_state);
+ return;
+ }
+ }
+
+ // Fall through for DCE2_SMB_DATA_STATE__SMB_HEADER
+ // This is the normal progression without segmentation.
+
+ // This state is to do validation checks on the SMB header and
+ // more importantly verify it's data that needs to be inspected.
+ // If the TID in the SMB header is not referring to the IPC share
+ // there won't be any DCE/RPC traffic associated with it.
+ case DCE2_SMB_DATA_STATE__SMB_HEADER:
+ {
+ // FIXIT-M add segmentation code path, including seg_buf code to the entire state
+
+ uint32_t data_need = (sizeof(NbssHdr) + sizeof(SmbNtHdr));
+ // See if there is enough data to process the SMB header
+ if (data_len < data_need)
+ {
+ DebugFormat(DEBUG_DCE_SMB, "Data len (%u) < "
+ "NetBIOS SS header + SMB header (%u). Queueing data.\n",
+ data_len, data_need);
+
+ // FIXIT-M add segmentation code path
+ return;
+ }
+
+ // FIXIT-M add segmentation checks
+ SmbNtHdr* smb_hdr = (SmbNtHdr*)(data_ptr + sizeof(NbssHdr));
+
+ // FIXIT-L Don't support SMB2 yet
+ if (SmbId(smb_hdr) == DCE2_SMB2_ID)
+ {
+ ssd->sd.flags |= DCE2_SSN_FLAG__NO_INSPECT;
+ return;
+ }
+
+ // See if this is something we need to inspect
+ rtracker = DCE2_SmbInspect(ssd, smb_hdr);
+ if (rtracker == nullptr)
+ {
+ DebugMessage(DEBUG_DCE_SMB, "Not inspecting SMB packet.\n");
+
+ // FIXIT-M add segmentation
+ *ignore_bytes = sizeof(NbssHdr) + NbssLen((NbssHdr*)data_ptr);
+
+ *data_state = DCE2_SMB_DATA_STATE__NETBIOS_HEADER;
+ dce2_smb_stats.smb_ignored_bytes += *ignore_bytes;
+ continue;
+ }
+
+ // Check the SMB header for anomolies
+ if (DCE2_SmbHdrChecks(ssd, smb_hdr) != DCE2_RET__SUCCESS)
+ {
+ DebugMessage(DEBUG_DCE_SMB, "Bad SMB header.\n");
+
+ *ignore_bytes = sizeof(NbssHdr) + NbssLen((NbssHdr*)data_ptr);
+
+ *data_state = DCE2_SMB_DATA_STATE__NETBIOS_HEADER;
+
+ dce2_smb_stats.smb_ignored_bytes += *ignore_bytes;
+ continue;
+ }
+
+ *data_state = DCE2_SMB_DATA_STATE__NETBIOS_PDU;
+ }
+
+ // Fall through
+
+ // This state ensures that we have the entire PDU before continuing
+ // to process.
+ case DCE2_SMB_DATA_STATE__NETBIOS_PDU:
+ {
+ uint32_t nb_len = NbssLen((NbssHdr*)data_ptr);
+ uint32_t data_need = sizeof(NbssHdr) + nb_len;
+
+ /* It's something we want to inspect so make sure we have the full NBSS packet */
+ if (data_len < data_need)
+ {
+ DebugFormat(DEBUG_DCE_SMB, "Data len(%u) < "
+ "NetBIOS SS header + NetBIOS len(%u). "
+ "Queueing data.\n", data_len, sizeof(NbssHdr) + nb_len);
+
+ // FIXIT-M add segmentation code
+
+ return;
+ }
+
+ // data_len >= data_need which means data_need <= UINT16_MAX
+ // So casts below of data_need to uint16_t are okay.
+
+ *data_state = DCE2_SMB_DATA_STATE__NETBIOS_HEADER;
+
+ const uint8_t* nb_ptr = data_ptr;
+ nb_len = data_need;
+ DCE2_MOVE(data_ptr, data_len, (uint16_t)data_need);
+
+ switch (ssd->pdu_state)
+ {
+ case DCE2_SMB_PDU_STATE__COMMAND:
+ {
+ SmbNtHdr* smb_hdr = (SmbNtHdr*)(nb_ptr + sizeof(NbssHdr));
+ DCE2_MOVE(nb_ptr, nb_len, (sizeof(NbssHdr) + sizeof(SmbNtHdr)));
+ ssd->cur_rtracker = (rtracker != nullptr)
+ ? rtracker : DCE2_SmbFindRequestTracker(ssd, smb_hdr);
+ if (ssd->cur_rtracker != nullptr)
+ DCE2_SmbProcessCommand(ssd, smb_hdr, nb_ptr, nb_len);
+ break;
+ }
+
+ case DCE2_SMB_PDU_STATE__RAW_DATA:
+ //FIXIT-M port raw state
+ break;
+ default:
+ DebugFormat(DEBUG_DCE_SMB, "%s(%d) Invalid SMB PDU "
+ "state: %d\n", __FILE__, __LINE__, ssd->pdu_state);
+ return;
+ }
+ break;
+ }
+
+ default:
+ DebugFormat(DEBUG_DCE_SMB, "%s(%d) Invalid SMB Data "
+ "state: %d\n", __FILE__, __LINE__, *data_state);
+ return;
+ }
+ }
+}
+
+/********************************************************************
+ * Function: DCE2_SmbInitGlobals()
+ *
+ * Purpose:
+ * Initializes global variables for SMB processing.
+ * Sets up the functions and valid word and byte counts for SMB
+ * commands.
+ * Sets up AndX chain mappings and valid command chaining for
+ * supported policies.
+ *
+ * Arguments: None
+ *
+ * Returns: None
+ *
+ ********************************************************************/
+void DCE2_SmbInitGlobals()
+{
+ memset(&smb_wcts, 0, sizeof(smb_wcts));
+ memset(&smb_bccs, 0, sizeof(smb_bccs));
+
+ // Sets up the function to call for the command and valid word and byte
+ // counts for the command. Ensuring valid word and byte counts is very
+ // important to processing the command as it will assume the command is
+ // legitimate and can access data that is acutally there. Note that
+ // commands with multiple word counts indicate a different command
+ // structure, however most, if not all just have an extended version
+ // of the structure for which the extended part isn't used. If the
+ // extended part of a command structure needs to be used, be sure to
+ // check the word count in the command function before accessing data
+ // in the extended version of the command structure.
+ for (int com = 0; com < SMB_MAX_NUM_COMS; com++)
+ {
+ switch (com)
+ {
+ case SMB_COM_OPEN:
+ // FIXIT-M port DCE2_SmbOpen. Same for other smb_com_funcs
+ smb_com_funcs[com] = DCE2_SmbOpen;
+
+ smb_deprecated_coms[com] = true;
+ smb_unusual_coms[com] = false;
+
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 2);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 7);
+
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 2, UINT16_MAX);
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
+ break;
+ case SMB_COM_CREATE:
+ smb_com_funcs[com] = DCE2_SmbCreate;
+
+ smb_deprecated_coms[com] = true;
+ smb_unusual_coms[com] = false;
+
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 3);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 1);
+
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 2, UINT16_MAX);
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
+ break;
+ case SMB_COM_CLOSE:
+ smb_com_funcs[com] = DCE2_SmbClose;
+ smb_deprecated_coms[com] = false;
+ smb_unusual_coms[com] = false;
+
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 3);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 0);
+
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, 0);
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
+ break;
+ case SMB_COM_RENAME:
+ smb_com_funcs[com] = DCE2_SmbRename;
+ smb_deprecated_coms[com] = false;
+ smb_unusual_coms[com] = false;
+
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 1);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 0);
+
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 4, UINT16_MAX);
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
+ break;
+ case SMB_COM_READ:
+ smb_com_funcs[com] = DCE2_SmbRead;
+ smb_deprecated_coms[com] = true;
+ smb_unusual_coms[com] = false;
+
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 5);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 5);
+
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, 0);
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 3, UINT16_MAX);
+ break;
+ case SMB_COM_WRITE:
+ smb_com_funcs[com] = DCE2_SmbWrite;
+ smb_deprecated_coms[com] = true;
+ smb_unusual_coms[com] = false;
+
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 5);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 1);
+
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 3, UINT16_MAX);
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
+ break;
+ case SMB_COM_CREATE_NEW:
+ smb_com_funcs[com] = DCE2_SmbCreateNew;
+ smb_deprecated_coms[com] = true;
+ smb_unusual_coms[com] = false;
+
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 3);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 1);
+
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 2, UINT16_MAX);
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
+ break;
+ case SMB_COM_LOCK_AND_READ:
+ smb_com_funcs[com] = DCE2_SmbLockAndRead;
+ smb_deprecated_coms[com] = true;
+ smb_unusual_coms[com] = false;
+
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 5);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 5);
+
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, 0);
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 3, UINT16_MAX);
+ break;
+ case SMB_COM_WRITE_AND_UNLOCK:
+ smb_com_funcs[com] = DCE2_SmbWriteAndUnlock;
+ smb_deprecated_coms[com] = true;
+ smb_unusual_coms[com] = false;
+
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 5);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 1);
+
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 3, UINT16_MAX);
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
+ break;
+ case SMB_COM_READ_RAW:
+ //smb_com_funcs[com] = DCE2_SmbReadRaw;
+ smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
+ smb_deprecated_coms[com] = true;
+ smb_unusual_coms[com] = false;
+
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 8);
+ // With optional OffsetHigh
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 10);
+
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, 0);
+ // Response is raw data, i.e. without SMB
+ break;
+ case SMB_COM_WRITE_RAW:
+ //smb_com_funcs[com] = DCE2_SmbWriteRaw;
+ smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
+ smb_deprecated_coms[com] = true;
+ smb_unusual_coms[com] = false;
+
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 12);
+ // With optional OffsetHigh
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 14);
+ // Interim server response
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 1);
+
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX);
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
+ break;
+ case SMB_COM_WRITE_COMPLETE:
+ // Final server response to SMB_COM_WRITE_RAW
+ //smb_com_funcs[com] = DCE2_SmbWriteComplete;
+ smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
+ smb_deprecated_coms[com] = true;
+ smb_unusual_coms[com] = false;
+
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 1);
+
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
+ break;
+ case SMB_COM_TRANSACTION:
+ smb_com_funcs[com] = DCE2_SmbTransaction;
+ smb_deprecated_coms[com] = false;
+ smb_unusual_coms[com] = false;
+
+ // Word count depends on setup count
+ //for (i = 14; i < 256; i++)
+ // DCE2_SmbSetValidWordCount(com, SMB_TYPE__REQUEST, i);
+ // In reality, all subcommands of SMB_COM_TRANSACTION requests
+ // have a setup count of 2 words.
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 16);
+
+ // \PIPE\LANMAN
+ // Not something the preprocessor is looking at as it
+ // doesn't carry DCE/RPC but don't want to false positive
+ // on the preprocessor event.
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 14);
+
+ // Word count depends on setup count
+ //for (i = 10; i < 256; i++)
+ // DCE2_SmbSetValidWordCount(com, SMB_TYPE__RESPONSE, i);
+ // In reality, all subcommands of SMB_COM_TRANSACTION responses
+ // have a setup count of 0 words.
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 10);
+
+ // Interim server response
+ // When client sends an incomplete transaction and needs to
+ // send TransactionSecondary requests to complete request.
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 0);
+
+ // Exception will be made for Interim responses when
+ // byte count is checked.
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX);
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX);
+ break;
+ case SMB_COM_TRANSACTION_SECONDARY:
+ //smb_com_funcs[com] = DCE2_SmbTransactionSecondary;
+ smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
+ smb_deprecated_coms[com] = false;
+ smb_unusual_coms[com] = false;
+
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 8);
+ // Response is an SMB_COM_TRANSACTION
+
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX);
+ break;
+ case SMB_COM_WRITE_AND_CLOSE:
+ //smb_com_funcs[com] = DCE2_SmbWriteAndClose;
+ smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
+ smb_deprecated_coms[com] = true;
+ smb_unusual_coms[com] = false;
+
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 6);
+ // For some reason MS-CIFS specifies a version of this command
+ // with 6 extra words (12 bytes) of reserved, i.e. useless data.
+ // Maybe had intentions of extending and defining the data at
+ // some point, but there is no documentation that I could find
+ // that does.
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 12);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 1);
+
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 1, UINT16_MAX);
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
+ break;
+ case SMB_COM_OPEN_ANDX:
+ smb_com_funcs[com] = DCE2_SmbOpenAndX;
+ smb_deprecated_coms[com] = true;
+ smb_unusual_coms[com] = false;
+
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 15);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 15);
+ // Extended response
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 19);
+
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 2, UINT16_MAX);
+ // MS-SMB says that Windows 2000, XP and Vista set this to
+ // some arbitrary value that is ignored on receipt.
+ //DCE2_SmbSetValidByteCount(com, SMB_TYPE__RESPONSE, 0, 0);
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX);
+ break;
+ case SMB_COM_READ_ANDX:
+ smb_com_funcs[com] = DCE2_SmbReadAndX;
+ smb_deprecated_coms[com] = false;
+ smb_unusual_coms[com] = false;
+
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 10);
+ // With optional OffsetHigh
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 12);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 12);
+
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, 0);
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX);
+ break;
+ case SMB_COM_WRITE_ANDX:
+ smb_com_funcs[com] = DCE2_SmbWriteAndX;
+ smb_deprecated_coms[com] = false;
+ smb_unusual_coms[com] = false;
+
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 12);
+ // With optional OffsetHigh
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 14);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 6);
+
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 1, UINT16_MAX);
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
+ break;
+ case SMB_COM_TRANSACTION2:
+ //smb_com_funcs[com] = DCE2_SmbTransaction2;
+ smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
+ smb_deprecated_coms[com] = false;
+ smb_unusual_coms[com] = false;
+
+ // Word count depends on setup count
+ //for (i = 14; i < 256; i++)
+ // DCE2_SmbSetValidWordCount(com, SMB_TYPE__REQUEST, i);
+ // In reality, all subcommands of SMB_COM_TRANSACTION2
+ // requests have a setup count of 1 word.
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 15);
+
+ // Word count depends on setup count
+ //for (i = 10; i < 256; i++)
+ // DCE2_SmbSetValidWordCount(com, SMB_TYPE__RESPONSE, i);
+ // In reality, all subcommands of SMB_COM_TRANSACTION2
+ // responses have a setup count of 0 or 1 word.
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 10);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 11);
+
+ // Interim server response
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 0);
+
+ // Exception will be made for Interim responses when
+ // byte count is checked.
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX);
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX);
+ break;
+ case SMB_COM_TRANSACTION2_SECONDARY:
+ //smb_com_funcs[com] = DCE2_SmbTransaction2Secondary;
+ smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
+ smb_deprecated_coms[com] = false;
+ smb_unusual_coms[com] = false;
+
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 9);
+ // Response is an SMB_COM_TRANSACTION2
+
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX);
+ break;
+ case SMB_COM_TREE_CONNECT:
+ smb_com_funcs[com] = DCE2_SmbTreeConnect;
+ smb_deprecated_coms[com] = true;
+ smb_unusual_coms[com] = false;
+
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 0);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 2);
+
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 6, UINT16_MAX);
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
+ break;
+ case SMB_COM_TREE_DISCONNECT:
+ smb_com_funcs[com] = DCE2_SmbTreeDisconnect;
+ smb_deprecated_coms[com] = false;
+ smb_unusual_coms[com] = false;
+
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 0);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 0);
+
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, 0);
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
+ break;
+ case SMB_COM_NEGOTIATE:
+ smb_com_funcs[com] = DCE2_SmbNegotiate;
+ smb_deprecated_coms[com] = false;
+ smb_unusual_coms[com] = false;
+
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 0);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 1);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 13);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 17);
+
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 2, UINT16_MAX);
+ // This can vary depending on dialect so just set wide.
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX);
+ break;
+ case SMB_COM_SESSION_SETUP_ANDX:
+ smb_com_funcs[com] = DCE2_SmbSessionSetupAndX;
+ smb_deprecated_coms[com] = false;
+ smb_unusual_coms[com] = false;
+
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 10);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 12);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 13);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 3);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 4);
+
+ // These can vary so just set wide.
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX);
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX);
+ break;
+ case SMB_COM_LOGOFF_ANDX:
+ smb_com_funcs[com] = DCE2_SmbLogoffAndX;
+ smb_deprecated_coms[com] = false;
+ smb_unusual_coms[com] = false;
+
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 2);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 2);
+ // Windows responds to a LogoffAndX => SessionSetupAndX with just a
+ // LogoffAndX and with the word count field containing 3, but only
+ // has 2 words
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 3);
+
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, 0);
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
+ break;
+ case SMB_COM_TREE_CONNECT_ANDX:
+ smb_com_funcs[com] = DCE2_SmbTreeConnectAndX;
+ smb_deprecated_coms[com] = false;
+ smb_unusual_coms[com] = false;
+
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 4);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 2);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 3);
+ // Extended response
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 7);
+
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 3, UINT16_MAX);
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 2, UINT16_MAX);
+ break;
+ case SMB_COM_NT_TRANSACT:
+ //smb_com_funcs[com] = DCE2_SmbNtTransact;
+ smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
+ smb_deprecated_coms[com] = false;
+ smb_unusual_coms[com] = false;
+
+ // Word count depends on setup count
+ // In reality, all subcommands of SMB_COM_NT_TRANSACT
+ // requests have a setup count of 0 or 4 words.
+ //for (i = 19; i < 256; i++)
+ // DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, i);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 19);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 23);
+
+ // Word count depends on setup count
+ // In reality, all subcommands of SMB_COM_NT_TRANSACT
+ // responses have a setup count of 0 or 1 word.
+ //for (i = 18; i < 256; i++)
+ // DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, i);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 18);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 19);
+
+ // Interim server response
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 0);
+
+ // Exception will be made for Interim responses when
+ // byte count is checked.
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX);
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX);
+ break;
+ case SMB_COM_NT_TRANSACT_SECONDARY:
+ //smb_com_funcs[com] = DCE2_SmbNtTransactSecondary;
+ smb_com_funcs[com] = DCE2_SmbComFuncPlaceholder;
+ smb_deprecated_coms[com] = false;
+ smb_unusual_coms[com] = false;
+
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 18);
+ // Response is an SMB_COM_NT_TRANSACT
+
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX);
+ break;
+ case SMB_COM_NT_CREATE_ANDX:
+ smb_com_funcs[com] = DCE2_SmbNtCreateAndX;
+ smb_deprecated_coms[com] = false;
+ smb_unusual_coms[com] = false;
+
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 24);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 34);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 26);
+ // Extended response - though there are actually 50 words
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 42);
+
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 2, UINT16_MAX);
+ // MS-SMB indicates that this field should be 0 but may be
+ // sent uninitialized so basically ignore it.
+ //DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0);
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX);
+ break;
+ default:
+ smb_com_funcs[com] = nullptr;
+ smb_deprecated_coms[com] = false;
+ smb_unusual_coms[com] = false;
+ // Just set to all valid since the specific command won't
+ // be processed. Don't want to false positive on these.
+ for (int i = 0; i < 256; i++)
+ {
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, (uint8_t)i);
+ DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, (uint8_t)i);
+ }
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX);
+ DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX);
+ break;
+ }
+ }
+
+ // Maps commands for use in quickly determining if a command
+ // is chainable and what command it is.
+ for (int com = 0; com < SMB_MAX_NUM_COMS; com++)
+ {
+ switch (com)
+ {
+ case SMB_COM_SESSION_SETUP_ANDX:
+ smb_chain_map[com] = SMB_ANDX_COM__SESSION_SETUP_ANDX;
+ break;
+ case SMB_COM_LOGOFF_ANDX:
+ smb_chain_map[com] = SMB_ANDX_COM__LOGOFF_ANDX;
+ break;
+ case SMB_COM_TREE_CONNECT_ANDX:
+ smb_chain_map[com] = SMB_ANDX_COM__TREE_CONNECT_ANDX;
+ break;
+ case SMB_COM_OPEN_ANDX:
+ smb_chain_map[com] = SMB_ANDX_COM__OPEN_ANDX;
+ break;
+ case SMB_COM_NT_CREATE_ANDX:
+ smb_chain_map[com] = SMB_ANDX_COM__NT_CREATE_ANDX;
+ break;
+ case SMB_COM_WRITE_ANDX:
+ smb_chain_map[com] = SMB_ANDX_COM__WRITE_ANDX;
+ break;
+ case SMB_COM_READ_ANDX:
+ smb_chain_map[com] = SMB_ANDX_COM__READ_ANDX;
+ break;
+ default:
+ smb_chain_map[com] = SMB_ANDX_COM__NONE;
+ break;
+ }
+ }
+
+ // Sets up the valid command chaining combinations per policy
+ for (int policy = 0; policy < DCE2_POLICY__MAX; policy++)
+ {
+ for (int andx = SMB_ANDX_COM__NONE; andx < SMB_ANDX_COM__MAX; andx++)
+ {
+ /* com is the chained command or com2 */
+ for (int com = 0; com < SMB_MAX_NUM_COMS; com++)
+ {
+ DCE2_SmbComFunc com_func = nullptr;
+
+ switch (policy)
+ {
+ case DCE2_POLICY__WIN2000:
+ case DCE2_POLICY__WINXP:
+ case DCE2_POLICY__WINVISTA:
+ case DCE2_POLICY__WIN2003:
+ case DCE2_POLICY__WIN2008:
+ case DCE2_POLICY__WIN7:
+ switch (andx)
+ {
+ case SMB_ANDX_COM__SESSION_SETUP_ANDX:
+ switch (com)
+ {
+ case SMB_COM_TREE_CONNECT_ANDX:
+ case SMB_COM_OPEN:
+ case SMB_COM_OPEN_ANDX:
+ case SMB_COM_CREATE:
+ case SMB_COM_CREATE_NEW:
+ com_func = smb_com_funcs[com];
+ break;
+ case SMB_COM_TRANSACTION:
+ if (policy == DCE2_POLICY__WIN2000)
+ com_func = smb_com_funcs[com];
+ break;
+ default:
+ break;
+ }
+ break;
+ case SMB_ANDX_COM__LOGOFF_ANDX:
+ switch (com)
+ {
+ case SMB_COM_SESSION_SETUP_ANDX:
+ case SMB_COM_TREE_CONNECT_ANDX: // Only for responses
+ com_func = smb_com_funcs[com];
+ break;
+ default:
+ break;
+ }
+ break;
+ case SMB_ANDX_COM__TREE_CONNECT_ANDX:
+ switch (com)
+ {
+ case SMB_COM_OPEN:
+ case SMB_COM_CREATE:
+ case SMB_COM_CREATE_NEW:
+ com_func = smb_com_funcs[com];
+ break;
+ case SMB_COM_TRANSACTION:
+ if (policy == DCE2_POLICY__WIN2000)
+ com_func = smb_com_funcs[com];
+ break;
+ default:
+ break;
+ }
+ break;
+ case SMB_ANDX_COM__OPEN_ANDX:
+ break;
+ case SMB_ANDX_COM__NT_CREATE_ANDX:
+ switch (com)
+ {
+ case SMB_COM_READ_ANDX: // Only for normal files
+ com_func = smb_com_funcs[com];
+ break;
+ default:
+ break;
+ }
+ break;
+ case SMB_ANDX_COM__WRITE_ANDX:
+ switch (com)
+ {
+ case SMB_COM_CLOSE:
+ case SMB_COM_WRITE_ANDX:
+ case SMB_COM_READ:
+ case SMB_COM_READ_ANDX:
+ com_func = smb_com_funcs[com];
+ break;
+ default:
+ break;
+ }
+ break;
+ case SMB_ANDX_COM__READ_ANDX:
+ break;
+ default:
+ break;
+ }
+ break;
+ case DCE2_POLICY__SAMBA:
+ case DCE2_POLICY__SAMBA_3_0_37:
+ case DCE2_POLICY__SAMBA_3_0_22:
+ case DCE2_POLICY__SAMBA_3_0_20:
+ switch (andx)
+ {
+ case SMB_ANDX_COM__SESSION_SETUP_ANDX:
+ switch (com)
+ {
+ case SMB_COM_LOGOFF_ANDX:
+ case SMB_COM_TREE_CONNECT:
+ case SMB_COM_TREE_CONNECT_ANDX:
+ case SMB_COM_TREE_DISCONNECT:
+ case SMB_COM_OPEN_ANDX:
+ case SMB_COM_NT_CREATE_ANDX:
+ case SMB_COM_CLOSE:
+ case SMB_COM_READ_ANDX:
+ com_func = smb_com_funcs[com];
+ break;
+ case SMB_COM_WRITE:
+ if ((policy == DCE2_POLICY__SAMBA_3_0_22)
+ || (policy == DCE2_POLICY__SAMBA_3_0_20))
+ com_func = smb_com_funcs[com];
+ break;
+ default:
+ break;
+ }
+ break;
+ case SMB_ANDX_COM__LOGOFF_ANDX:
+ switch (com)
+ {
+ case SMB_COM_SESSION_SETUP_ANDX:
+ case SMB_COM_TREE_DISCONNECT:
+ com_func = smb_com_funcs[com];
+ break;
+ default:
+ break;
+ }
+ break;
+ case SMB_ANDX_COM__TREE_CONNECT_ANDX:
+ switch (com)
+ {
+ case SMB_COM_SESSION_SETUP_ANDX:
+ case SMB_COM_LOGOFF_ANDX:
+ case SMB_COM_TREE_DISCONNECT:
+ case SMB_COM_OPEN_ANDX:
+ case SMB_COM_NT_CREATE_ANDX:
+ case SMB_COM_CLOSE:
+ case SMB_COM_WRITE:
+ case SMB_COM_READ_ANDX:
+ com_func = smb_com_funcs[com];
+ break;
+ default:
+ break;
+ }
+ break;
+ case SMB_ANDX_COM__OPEN_ANDX:
+ switch (com)
+ {
+ case SMB_COM_SESSION_SETUP_ANDX:
+ case SMB_COM_LOGOFF_ANDX:
+ case SMB_COM_TREE_CONNECT:
+ case SMB_COM_TREE_CONNECT_ANDX:
+ case SMB_COM_TREE_DISCONNECT:
+ case SMB_COM_OPEN_ANDX:
+ case SMB_COM_NT_CREATE_ANDX:
+ case SMB_COM_CLOSE:
+ case SMB_COM_WRITE:
+ case SMB_COM_READ_ANDX:
+ com_func = smb_com_funcs[com];
+ break;
+ default:
+ break;
+ }
+ break;
+ case SMB_ANDX_COM__NT_CREATE_ANDX:
+ switch (com)
+ {
+ case SMB_COM_SESSION_SETUP_ANDX:
+ case SMB_COM_TREE_CONNECT:
+ case SMB_COM_TREE_CONNECT_ANDX:
+ case SMB_COM_OPEN_ANDX:
+ case SMB_COM_NT_CREATE_ANDX:
+ case SMB_COM_WRITE:
+ case SMB_COM_READ_ANDX:
+ com_func = smb_com_funcs[com];
+ break;
+ case SMB_COM_LOGOFF_ANDX:
+ case SMB_COM_TREE_DISCONNECT:
+ case SMB_COM_CLOSE:
+ if ((policy == DCE2_POLICY__SAMBA)
+ || (policy == DCE2_POLICY__SAMBA_3_0_37))
+ com_func = smb_com_funcs[com];
+ break;
+ default:
+ break;
+ }
+ break;
+ case SMB_ANDX_COM__WRITE_ANDX:
+ switch (com)
+ {
+ case SMB_COM_SESSION_SETUP_ANDX:
+ case SMB_COM_LOGOFF_ANDX:
+ case SMB_COM_TREE_CONNECT:
+ case SMB_COM_TREE_CONNECT_ANDX:
+ case SMB_COM_OPEN_ANDX:
+ case SMB_COM_NT_CREATE_ANDX:
+ case SMB_COM_CLOSE:
+ case SMB_COM_WRITE:
+ case SMB_COM_READ_ANDX:
+ case SMB_COM_WRITE_ANDX:
+ com_func = smb_com_funcs[com];
+ break;
+ default:
break;
}
break;
case SMB_ANDX_COM__READ_ANDX:
+ switch (com)
+ {
+ case SMB_COM_SESSION_SETUP_ANDX:
+ case SMB_COM_WRITE:
+ com_func = smb_com_funcs[com];
+ break;
+ case SMB_COM_LOGOFF_ANDX:
+ case SMB_COM_TREE_CONNECT:
+ case SMB_COM_TREE_CONNECT_ANDX:
+ case SMB_COM_TREE_DISCONNECT:
+ case SMB_COM_OPEN_ANDX:
+ case SMB_COM_NT_CREATE_ANDX:
+ case SMB_COM_CLOSE:
+ case SMB_COM_READ_ANDX:
+ if ((policy == DCE2_POLICY__SAMBA)
+ || (policy == DCE2_POLICY__SAMBA_3_0_37))
+ com_func = smb_com_funcs[com];
+ break;
+ default:
+ break;
+ }
break;
default:
break;
}
break;
- case DCE2_POLICY__SAMBA:
- case DCE2_POLICY__SAMBA_3_0_37:
- case DCE2_POLICY__SAMBA_3_0_22:
- case DCE2_POLICY__SAMBA_3_0_20:
- switch (andx)
+ default:
+ break;
+ }
+
+ smb_chain_funcs[policy][andx][com] = com_func;
+ }
+ }
+ }
+}
+
+// Convenience function to determine whether or not the transaction is complete
+// for one side, i.e. all data and parameters sent.
+static inline bool DCE2_SmbIsTransactionComplete(DCE2_SmbTransactionTracker* ttracker)
+{
+ if ((ttracker->tdcnt == ttracker->dsent)
+ && (ttracker->tpcnt == ttracker->psent))
+ return true;
+ return false;
+}
+
+/********************************************************************
+ * Function: DCE2_SmbCheckAndXOffset()
+ *
+ * Purpose:
+ * Validates that the AndXOffset is within bounds of the remaining
+ * data we have to work with.
+ *
+ * Arguments:
+ * uint8_t * - pointer to where the offset would take us.
+ * uint8_t * - pointer to bound offset
+ * uint8_t * - length of data where offset should be within
+ *
+ * Returns:
+ * DCE2_RET__SUCCESS - Offset is okay.
+ * DCE2_RET__ERROR - Offset is bad.
+ *
+ ********************************************************************/
+static inline DCE2_Ret DCE2_SmbCheckAndXOffset(const uint8_t* off_ptr, const uint8_t* start_bound,
+ const uint32_t length)
+{
+ /* Offset should not point within data we just looked at or be equal to
+ * or beyond the length of the NBSS length left */
+ if ((off_ptr < start_bound) ||
+ (off_ptr > (start_bound + length)))
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_BAD_OFF, (dce2CommonStats*)&dce2_smb_stats);
+
+ return DCE2_RET__ERROR;
+ }
+
+ return DCE2_RET__SUCCESS;
+}
+
+static void DCE2_SmbQueueTmpFileTracker(DCE2_SmbSsnData* ssd,
+ DCE2_SmbRequestTracker* rtracker, const uint16_t uid, const uint16_t tid)
+{
+ Profile profile(dce2_smb_pstat_smb_fid);
+
+ DebugFormat(DEBUG_DCE_SMB, "Queueing file tracker "
+ "with Uid: %u, Tid: %u\n", uid, tid);
+
+ DCE2_SmbFileTracker* ftracker = (DCE2_SmbFileTracker*)
+ SnortAlloc(sizeof(DCE2_SmbFileTracker));
+
+ if (ftracker == nullptr)
+ {
+ return;
+ }
+
+ bool is_ipc = DCE2_SmbIsTidIPC(ssd, tid);
+ if (DCE2_SmbInitFileTracker(ssd, ftracker, is_ipc, uid, tid, DCE2_SENTINEL) !=
+ DCE2_RET__SUCCESS)
+ {
+ DCE2_SmbCleanFileTracker(ftracker);
+ free((void*)ftracker);
+ return;
+ }
+
+ if (!is_ipc && (ssd->fapi_ftracker == ftracker))
+ ssd->fapi_ftracker = nullptr;
+
+ if (rtracker->ft_queue == nullptr)
+ {
+ rtracker->ft_queue = DCE2_QueueNew(DCE2_SmbFileTrackerDataFree);
+ if (rtracker->ft_queue == nullptr)
+ {
+ DCE2_SmbCleanSessionFileTracker(ssd, ftracker);
+ return;
+ }
+ }
+
+ if (DCE2_QueueEnqueue(rtracker->ft_queue, (void*)ftracker) != DCE2_RET__SUCCESS)
+ {
+ DCE2_SmbCleanSessionFileTracker(ssd, ftracker);
+ return;
+ }
+}
+
+/********************************************************************
+ * Function: DCE2_SmbInvalidShareCheck()
+ *
+ * Purpose:
+ * Checks the share reported in a TreeConnect or TreeConnectAndX
+ * against the invalid share list configured in the dcerpc2
+ * configuration in snort.conf.
+ *
+ * Arguments:
+ * DCE2_SmbSsnData * - SMB session data structure
+ * SmbNtHdr * - pointer to the SMB header structure
+ * uint8_t * - current pointer to the share to check
+ * uint32_t - the remaining length
+ *
+ * Returns: None
+ * Alerts if there is an invalid share match.
+ *
+ ********************************************************************/
+static void DCE2_SmbInvalidShareCheck(DCE2_SmbSsnData* ssd,
+ const SmbNtHdr* smb_hdr, const uint8_t* nb_ptr, uint32_t nb_len)
+{
+ DCE2_List* share_list = DCE2_ScSmbInvalidShares((dce2SmbProtoConf*)ssd->sd.config);
+ if (share_list == nullptr)
+ return;
+
+ dce2SmbShare* smb_share;
+ for (smb_share = (dce2SmbShare*)DCE2_ListFirst(share_list);
+ smb_share != nullptr;
+ smb_share = (dce2SmbShare*)DCE2_ListNext(share_list))
+ {
+ unsigned int i;
+ const char* share_str;
+ unsigned int share_str_len;
+
+ if (SmbUnicode(smb_hdr))
+ {
+ share_str = smb_share->unicode_str;
+ share_str_len = smb_share->unicode_str_len;
+ }
+ else
+ {
+ share_str = smb_share->ascii_str;
+ share_str_len = smb_share->ascii_str_len;
+ }
+
+ /* Make sure we have enough data */
+ if (nb_len < share_str_len)
+ continue;
+
+ /* Test for share match */
+ for (i = 0; i < share_str_len; i++)
+ {
+ /* All share strings should have been converted to upper case and
+ * should include null terminating bytes */
+ if ((nb_ptr[i] != share_str[i]) && (nb_ptr[i] != tolower((int)share_str[i])))
+ break;
+ }
+
+ if (i == share_str_len)
+ {
+ /* Should only match one share since no duplicate shares in list */
+ dce_alert(GID_DCE2, DCE2_SMB_INVALID_SHARE, (dce2CommonStats*)&dce2_smb_stats);
+ break;
+ }
+ }
+}
+
+/********************************************************************
+ * Functions:
+ * DCE2_SmbOpen()
+ * DCE2_SmbCreate()
+ * DCE2_SmbClose()
+ * DCE2_SmbRename()
+ * DCE2_SmbRead()
+ * DCE2_SmbWrite()
+ * DCE2_SmbCreateNew()
+ * DCE2_SmbLockAndRead()
+ * DCE2_SmbWriteAndUnlock()
+ * DCE2_SmbReadRaw()
+ * DCE2_SmbWriteRaw()
+ * DCE2_SmbWriteComplete()
+ * DCE2_SmbTransaction()
+ * DCE2_SmbTransactionSecondary()
+ * DCE2_SmbWriteAndClose()
+ * DCE2_SmbOpenAndX()
+ * DCE2_SmbReadAndX()
+ * DCE2_SmbWriteAndX()
+ * DCE2_SmbTransaction2()
+ * DCE2_SmbTransaction2Secondary()
+ * DCE2_SmbTreeConnect()
+ * DCE2_SmbTreeDisconnect()
+ * DCE2_SmbNegotiate()
+ * DCE2_SmbSessionSetupAndX()
+ * DCE2_SmbLogoffAndX()
+ * DCE2_SmbTreeConnectAndX()
+ * DCE2_SmbNtTransact()
+ * DCE2_SmbNtTransactSecondary()
+ * DCE2_SmbNtCreateAndX()
+ *
+ * Purpose: Process SMB command
+ *
+ * Arguments:
+ * DCE2_SmbSsnData * - SMB session data structure
+ * const SmbNtHdr * - SMB header structure (packet pointer)
+ * const DCE2_SmbComInfo * - Basic command information structure
+ * uint8_t * - pointer to start of command (packet pointer)
+ * uint32_t - remaining NetBIOS length
+ *
+ * Returns:
+ * DCE2_Ret - DCE2_RET__ERROR if something went wrong and/or processing
+ * should stop
+ * DCE2_RET__SUCCESS if processing should continue
+ *
+ ********************************************************************/
+
+// SMB_COM_OPEN
+static DCE2_Ret DCE2_SmbOpen(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
+ const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
+{
+ if (!DCE2_ComInfoCanProcessCommand(com_info))
+ return DCE2_RET__ERROR;
+
+ if (DCE2_ComInfoIsResponse(com_info))
+ {
+ DCE2_SmbFileTracker* ftracker;
+
+ if (!DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid)
+ && (SmbFileAttrsDirectory(SmbOpenRespFileAttrs((SmbOpenResp*)nb_ptr))
+ || SmbOpenForWriting(SmbOpenRespAccessMode((SmbOpenResp*)nb_ptr))))
+ return DCE2_RET__SUCCESS;
+
+ ftracker = DCE2_SmbNewFileTracker(ssd, ssd->cur_rtracker->uid,
+ ssd->cur_rtracker->tid, SmbOpenRespFid((SmbOpenResp*)nb_ptr));
+ if (ftracker == nullptr)
+ return DCE2_RET__ERROR;
+
+ ftracker->file_name = ssd->cur_rtracker->file_name;
+ ssd->cur_rtracker->file_name = nullptr;
+
+ if (!ftracker->is_ipc)
+ {
+ // This command can only be used to open an existing file
+ ftracker->ff_file_size = SmbOpenRespFileSize((SmbOpenResp*)nb_ptr);
+ }
+ }
+ else
+ {
+ // Have at least 2 bytes of data based on byte count check done earlier
+
+ DCE2_MOVE(nb_ptr, nb_len, DCE2_ComInfoCommandSize(com_info));
+
+ if (!SmbFmtAscii(*nb_ptr))
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_BAD_FORM, (dce2CommonStats*)&dce2_smb_stats);
+ return DCE2_RET__ERROR;
+ }
+
+ DCE2_MOVE(nb_ptr, nb_len, 1);
+
+ ssd->cur_rtracker->file_name =
+ DCE2_SmbGetString(nb_ptr, nb_len, SmbUnicode(smb_hdr), false);
+ }
+
+ return DCE2_RET__SUCCESS;
+}
+
+// SMB_COM_CREATE
+static DCE2_Ret DCE2_SmbCreate(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
+ const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
+{
+ if (!DCE2_ComInfoCanProcessCommand(com_info))
+ return DCE2_RET__ERROR;
+
+ if (DCE2_ComInfoIsResponse(com_info))
+ {
+ DCE2_SmbFileTracker* ftracker = DCE2_SmbNewFileTracker(
+ ssd, ssd->cur_rtracker->uid, ssd->cur_rtracker->tid,
+ SmbCreateRespFid((SmbCreateResp*)nb_ptr));
+
+ if (ftracker == nullptr)
+ return DCE2_RET__ERROR;
+
+ ftracker->file_name = ssd->cur_rtracker->file_name;
+ ssd->cur_rtracker->file_name = nullptr;
+
+ // Command creates or opens and truncates file to 0 so assume
+ // upload.
+ if (!ftracker->is_ipc)
+ ftracker->ff_file_direction = DCE2_SMB_FILE_DIRECTION__UPLOAD;
+ }
+ else
+ {
+ if (!DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid))
+ {
+ uint16_t file_attrs = SmbCreateReqFileAttrs((SmbCreateReq*)nb_ptr);
+
+ if (SmbAttrDirectory(file_attrs))
+ return DCE2_RET__IGNORE;
+
+ if (SmbEvasiveFileAttrs(file_attrs))
+ dce_alert(GID_DCE2, DCE2_SMB_EVASIVE_FILE_ATTRS,
+ (dce2CommonStats*)&dce2_smb_stats);
+ }
+
+ // Have at least 2 bytes of data based on byte count check done earlier
+
+ DCE2_MOVE(nb_ptr, nb_len, DCE2_ComInfoCommandSize(com_info));
+
+ if (!SmbFmtAscii(*nb_ptr))
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_BAD_FORM, (dce2CommonStats*)&dce2_smb_stats);
+ return DCE2_RET__ERROR;
+ }
+
+ DCE2_MOVE(nb_ptr, nb_len, 1);
+
+ ssd->cur_rtracker->file_name =
+ DCE2_SmbGetString(nb_ptr, nb_len, SmbUnicode(smb_hdr), false);
+ }
+
+ return DCE2_RET__SUCCESS;
+}
+
+// SMB_COM_CLOSE
+static DCE2_Ret DCE2_SmbClose(DCE2_SmbSsnData* ssd, const SmbNtHdr*,
+ const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t)
+{
+ if (!DCE2_ComInfoCanProcessCommand(com_info))
+ return DCE2_RET__ERROR;
+
+ if (DCE2_ComInfoIsRequest(com_info))
+ {
+ uint16_t fid = SmbCloseReqFid((SmbCloseReq*)nb_ptr);
+
+ // Set this for response
+ ssd->cur_rtracker->ftracker = DCE2_SmbGetFileTracker(ssd, fid);
+
+ //FIXIT-M port active response related code
+/*
+#ifdef ACTIVE_RESPONSE
+ if ((ssd->fb_ftracker != NULL) && (ssd->fb_ftracker == ssd->cur_rtracker->ftracker))
+ {
+ void *ssnptr = ssd->sd.wire_pkt->stream_session;
+ void *p = (void *)ssd->sd.wire_pkt;
+ File_Verdict verdict = DCE2_SmbGetFileVerdict(p, ssnptr);
+
+ if ((verdict == FILE_VERDICT_BLOCK) || (verdict == FILE_VERDICT_REJECT))
+ ssd->block_pdus = true;
+ }
+#endif
+*/
+ }
+ else
+ {
+ DCE2_SmbRemoveFileTracker(ssd, ssd->cur_rtracker->ftracker);
+ }
+
+ return DCE2_RET__SUCCESS;
+}
+
+// SMB_COM_RENAME
+static DCE2_Ret DCE2_SmbRename(DCE2_SmbSsnData*, const SmbNtHdr* smb_hdr,
+ const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
+{
+ // NOTE: This command is only processed for CVE-2006-4696 where the buffer
+ // formats are invalid and has no bearing on DCE/RPC processing.
+
+ if (!DCE2_ComInfoCanProcessCommand(com_info))
+ return DCE2_RET__ERROR;
+
+ if (DCE2_ComInfoIsRequest(com_info))
+ {
+ // Have at least 4 bytes of data based on byte count check done earlier
+
+ uint32_t i;
+
+ DCE2_MOVE(nb_ptr, nb_len, DCE2_ComInfoCommandSize(com_info));
+
+ if (!SmbFmtAscii(*nb_ptr))
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_BAD_FORM, (dce2CommonStats*)&dce2_smb_stats);
+ return DCE2_RET__ERROR;
+ }
+
+ DCE2_MOVE(nb_ptr, nb_len, 1);
+
+ if (SmbUnicode(smb_hdr))
+ {
+ for (i = 0; i < (nb_len - 1); i += 2)
+ {
+ if (*((uint16_t*)(nb_ptr + i)) == 0)
+ {
+ i += 2; // move past null terminating bytes
+ break;
+ }
+ }
+ }
+ else
+ {
+ for (i = 0; i < nb_len; i++)
+ {
+ if (nb_ptr[i] == 0)
+ {
+ i++; // move past null terminating byte
+ break;
+ }
+ }
+ }
+
+ // i <= nb_len
+ DCE2_MOVE(nb_ptr, nb_len, i);
+
+ if ((nb_len > 0) && !SmbFmtAscii(*nb_ptr))
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_BAD_FORM, (dce2CommonStats*)&dce2_smb_stats);
+ return DCE2_RET__ERROR;
+ }
+ }
+
+ // Don't care about tracking response
+ return DCE2_RET__ERROR;
+}
+
+// SMB_COM_READ
+static DCE2_Ret DCE2_SmbRead(DCE2_SmbSsnData* ssd, const SmbNtHdr*,
+ const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
+{
+ if (!DCE2_ComInfoCanProcessCommand(com_info))
+ return DCE2_RET__ERROR;
+
+ if (DCE2_ComInfoIsRequest(com_info))
+ {
+ DCE2_SmbFileTracker* ftracker =
+ DCE2_SmbGetFileTracker(ssd, SmbReadReqFid((SmbReadReq*)nb_ptr));
+
+ // Set this for response since response doesn't have the Fid
+ ssd->cur_rtracker->ftracker = ftracker;
+ if ((ftracker != nullptr) && !ftracker->is_ipc)
+ ssd->cur_rtracker->file_offset = SmbReadReqOffset((SmbReadReq*)nb_ptr);
+ }
+ else
+ {
+ // Have at least 3 bytes of data based on byte count check done earlier
+
+ uint16_t com_size = DCE2_ComInfoCommandSize(com_info);
+ uint16_t byte_count = DCE2_ComInfoByteCount(com_info);
+ uint16_t com_dcnt = SmbReadRespCount((SmbReadResp*)nb_ptr);
+ uint8_t fmt = *(nb_ptr + com_size);
+ uint16_t fmt_dcnt = alignedNtohs((uint16_t*)(nb_ptr + com_size + 1));
+
+ DCE2_MOVE(nb_ptr, nb_len, (com_size + 3));
+
+ DCE2_SmbCheckFmtData(ssd, nb_len, byte_count, fmt, com_dcnt, fmt_dcnt);
+
+ if (com_dcnt > nb_len)
+ return DCE2_RET__ERROR;
+
+ return DCE2_SmbProcessResponseData(ssd, nb_ptr, com_dcnt);
+ }
+
+ return DCE2_RET__SUCCESS;
+}
+
+// SMB_COM_WRITE
+static DCE2_Ret DCE2_SmbWrite(DCE2_SmbSsnData* ssd, const SmbNtHdr*,
+ const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
+{
+ if (!DCE2_ComInfoCanProcessCommand(com_info))
+ return DCE2_RET__ERROR;
+
+ if (DCE2_ComInfoIsRequest(com_info))
+ {
+ // Have at least 3 bytes of data based on byte count check done earlier
+
+ uint16_t com_size = DCE2_ComInfoCommandSize(com_info);
+ uint16_t byte_count = DCE2_ComInfoByteCount(com_info);
+ uint8_t fmt = *(nb_ptr + com_size);
+ uint16_t com_dcnt = SmbWriteReqCount((SmbWriteReq*)nb_ptr);
+ uint16_t fmt_dcnt = alignedNtohs((uint16_t*)(nb_ptr + com_size + 1));
+ uint16_t fid = SmbWriteReqFid((SmbWriteReq*)nb_ptr);
+ uint32_t offset = SmbWriteReqOffset((SmbWriteReq*)nb_ptr);
+
+ DCE2_MOVE(nb_ptr, nb_len, (com_size + 3));
+
+ DCE2_SmbCheckFmtData(ssd, nb_len, byte_count, fmt, com_dcnt, fmt_dcnt);
+
+ if (com_dcnt == 0)
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_DCNT_ZERO, (dce2CommonStats*)&dce2_smb_stats);
+ return DCE2_RET__ERROR;
+ }
+
+ if (com_dcnt > nb_len)
+ com_dcnt = (uint16_t)nb_len;
+
+ return DCE2_SmbProcessRequestData(ssd, fid, nb_ptr, com_dcnt, offset);
+ }
+
+ return DCE2_RET__SUCCESS;
+}
+
+// SMB_COM_CREATE_NEW
+static DCE2_Ret DCE2_SmbCreateNew(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
+ const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
+{
+ if (!DCE2_ComInfoCanProcessCommand(com_info))
+ return DCE2_RET__ERROR;
+
+ if (DCE2_ComInfoIsResponse(com_info))
+ {
+ DCE2_SmbFileTracker* ftracker = DCE2_SmbNewFileTracker(
+ ssd, ssd->cur_rtracker->uid, ssd->cur_rtracker->tid,
+ SmbCreateNewRespFid((SmbCreateNewResp*)nb_ptr));
+
+ if (ftracker == nullptr)
+ return DCE2_RET__ERROR;
+
+ ftracker->file_name = ssd->cur_rtracker->file_name;
+ ssd->cur_rtracker->file_name = nullptr;
+
+ // Command creates a new file so assume upload.
+ if (!ftracker->is_ipc)
+ ftracker->ff_file_direction = DCE2_SMB_FILE_DIRECTION__UPLOAD;
+ }
+ else
+ {
+ if (!DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid))
+ {
+ uint16_t file_attrs = SmbCreateNewReqFileAttrs((SmbCreateNewReq*)nb_ptr);
+
+ if (SmbAttrDirectory(file_attrs))
+ return DCE2_RET__IGNORE;
+
+ if (SmbEvasiveFileAttrs(file_attrs))
+ dce_alert(GID_DCE2, DCE2_SMB_EVASIVE_FILE_ATTRS,
+ (dce2CommonStats*)&dce2_smb_stats);
+ }
+
+ // Have at least 2 bytes of data based on byte count check done earlier
+
+ DCE2_MOVE(nb_ptr, nb_len, DCE2_ComInfoCommandSize(com_info));
+
+ if (!SmbFmtAscii(*nb_ptr))
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_BAD_FORM, (dce2CommonStats*)&dce2_smb_stats);
+ return DCE2_RET__ERROR;
+ }
+
+ DCE2_MOVE(nb_ptr, nb_len, 1);
+
+ ssd->cur_rtracker->file_name =
+ DCE2_SmbGetString(nb_ptr, nb_len, SmbUnicode(smb_hdr), false);
+ }
+
+ return DCE2_RET__SUCCESS;
+}
+
+// SMB_COM_LOCK_AND_READ
+static DCE2_Ret DCE2_SmbLockAndRead(DCE2_SmbSsnData* ssd, const SmbNtHdr*,
+ const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
+{
+ if (!DCE2_ComInfoCanProcessCommand(com_info))
+ return DCE2_RET__ERROR;
+
+ if (DCE2_ComInfoIsRequest(com_info))
+ {
+ DCE2_SmbFileTracker* ftracker =
+ DCE2_SmbFindFileTracker(ssd, ssd->cur_rtracker->uid,
+ ssd->cur_rtracker->tid, SmbLockAndReadReqFid((SmbLockAndReadReq*)nb_ptr));
+
+ // No sense in tracking response
+ if (ftracker == nullptr)
+ return DCE2_RET__ERROR;
+
+ if (!ftracker->is_ipc)
+ ssd->cur_rtracker->file_offset = SmbLockAndReadReqOffset((SmbLockAndReadReq*)nb_ptr);
+
+ // Set this for response
+ ssd->cur_rtracker->ftracker = ftracker;
+ }
+ else
+ {
+ // Have at least 3 bytes of data based on byte count check done earlier
+
+ uint16_t com_size = DCE2_ComInfoCommandSize(com_info);
+ uint16_t byte_count = DCE2_ComInfoByteCount(com_info);
+ uint8_t fmt = *(nb_ptr + com_size);
+ uint16_t com_dcnt = SmbLockAndReadRespCount((SmbLockAndReadResp*)nb_ptr);
+ uint16_t fmt_dcnt = alignedNtohs((uint16_t*)(nb_ptr + com_size + 1));
+
+ DCE2_MOVE(nb_ptr, nb_len, (com_size + 3));
+
+ DCE2_SmbCheckFmtData(ssd, nb_len, byte_count, fmt, com_dcnt, fmt_dcnt);
+
+ DebugFormat(DEBUG_DCE_SMB," SmbWriteLockAndRead dcnt %d\n", com_dcnt);
+ if (com_dcnt == 0)
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_DCNT_ZERO, (dce2CommonStats*)&dce2_smb_stats);
+ return DCE2_RET__ERROR;
+ }
+
+ if (com_dcnt > nb_len)
+ com_dcnt = (uint16_t)nb_len;
+
+ return DCE2_SmbProcessResponseData(ssd, nb_ptr, com_dcnt);
+ }
+
+ return DCE2_RET__SUCCESS;
+}
+
+// SMB_COM_WRITE_AND_UNLOCK
+static DCE2_Ret DCE2_SmbWriteAndUnlock(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
+ const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
+{
+ if (!DCE2_ComInfoCanProcessCommand(com_info))
+ {
+ if (DCE2_ComInfoIsBadLength(com_info) || DCE2_ComInfoIsInvalidWordCount(com_info))
+ return DCE2_RET__ERROR;
+
+ // These are special cases. The write succeeds but the unlock fails
+ // so an error reponse is returned but the data was actually written.
+ if (DCE2_ComInfoIsResponse(com_info) && DCE2_ComInfoIsStatusError(com_info))
+ {
+ if (DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid))
+ {
+ if (!SmbErrorInvalidDeviceRequest(smb_hdr))
+ return DCE2_RET__ERROR;
+ }
+ else if (!SmbErrorRangeNotLocked(smb_hdr))
+ {
+ return DCE2_RET__ERROR;
+ }
+ }
+ }
+
+ if (DCE2_ComInfoIsRequest(com_info))
+ {
+ // Have at least 3 bytes of data based on byte count check done earlier
+
+ uint16_t com_size = DCE2_ComInfoCommandSize(com_info);
+ uint16_t byte_count = DCE2_ComInfoByteCount(com_info);
+ uint8_t fmt = *(nb_ptr + com_size);
+ uint16_t com_dcnt = SmbWriteAndUnlockReqCount((SmbWriteAndUnlockReq*)nb_ptr);
+ uint16_t fmt_dcnt = alignedNtohs((uint16_t*)(nb_ptr + com_size + 1));
+ uint16_t fid = SmbWriteAndUnlockReqFid((SmbWriteAndUnlockReq*)nb_ptr);
+ uint32_t offset = SmbWriteAndUnlockReqOffset((SmbWriteAndUnlockReq*)nb_ptr);
+
+ DCE2_MOVE(nb_ptr, nb_len, (com_size + 3));
+
+ DCE2_SmbCheckFmtData(ssd, nb_len, byte_count, fmt, com_dcnt, fmt_dcnt);
+
+ if (com_dcnt == 0)
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_DCNT_ZERO, (dce2CommonStats*)&dce2_smb_stats);
+ return DCE2_RET__ERROR;
+ }
+
+ if (com_dcnt > nb_len)
+ com_dcnt = (uint16_t)nb_len;
+
+ return DCE2_SmbProcessRequestData(ssd, fid, nb_ptr, com_dcnt, offset);
+ }
+
+ return DCE2_RET__SUCCESS;
+}
+
+// SMB_COM_OPEN_ANDX
+static DCE2_Ret DCE2_SmbOpenAndX(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
+ const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
+{
+ if (!DCE2_ComInfoCanProcessCommand(com_info))
+ return DCE2_RET__ERROR;
+
+ if (DCE2_ComInfoIsResponse(com_info))
+ {
+ const uint16_t fid = SmbOpenAndXRespFid((SmbOpenAndXResp*)nb_ptr);
+ const uint16_t file_attrs = SmbOpenAndXRespFileAttrs((SmbOpenAndXResp*)nb_ptr);
+ const uint16_t resource_type = SmbOpenAndXRespResourceType((SmbOpenAndXResp*)nb_ptr);
+ DCE2_SmbFileTracker* ftracker = nullptr;
+
+ // Set request tracker's current file tracker in case of chained commands
+ switch (SmbAndXCom2((SmbAndXCommon*)nb_ptr))
+ {
+ // This is in case in the request a write was chained to an open
+ // in which case the write will be to the newly opened file
+ case SMB_COM_WRITE:
+ case SMB_COM_WRITE_ANDX:
+ case SMB_COM_TRANSACTION:
+ case SMB_COM_READ_ANDX:
+ ftracker = DCE2_SmbDequeueTmpFileTracker(ssd, ssd->cur_rtracker, fid);
+ break;
+ default:
+ break;
+ }
+
+ if (!DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid)
+ && (SmbFileAttrsDirectory(file_attrs)
+ || !SmbResourceTypeDisk(resource_type)))
+ {
+ if (ftracker != nullptr)
+ DCE2_SmbRemoveFileTracker(ssd, ftracker);
+ return DCE2_RET__SUCCESS;
+ }
+
+ if (ftracker == nullptr)
+ {
+ ftracker = DCE2_SmbNewFileTracker(ssd,
+ ssd->cur_rtracker->uid, ssd->cur_rtracker->tid, fid);
+ if (ftracker == nullptr)
+ return DCE2_RET__ERROR;
+ }
+
+ ftracker->file_name = ssd->cur_rtracker->file_name;
+ ssd->cur_rtracker->file_name = nullptr;
+
+ if (!ftracker->is_ipc)
+ {
+ const uint16_t open_results = SmbOpenAndXRespOpenResults((SmbOpenAndXResp*)nb_ptr);
+
+ if (SmbOpenResultRead(open_results))
+ {
+ ftracker->ff_file_size = SmbOpenAndXRespFileSize((SmbOpenAndXResp*)nb_ptr);
+ }
+ else
+ {
+ ftracker->ff_file_size = ssd->cur_rtracker->file_size;
+ ftracker->ff_file_direction = DCE2_SMB_FILE_DIRECTION__UPLOAD;
+ }
+ }
+
+ ssd->cur_rtracker->ftracker = ftracker;
+ }
+ else
+ {
+ uint32_t pad = 0;
+ const bool unicode = SmbUnicode(smb_hdr);
+ uint8_t null_bytes = unicode ? 2 : 1;
+
+ if (!DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid))
+ {
+ uint16_t file_attrs = SmbOpenAndXReqFileAttrs((SmbOpenAndXReq*)nb_ptr);
+
+ if (SmbEvasiveFileAttrs(file_attrs))
+ dce_alert(GID_DCE2, DCE2_SMB_EVASIVE_FILE_ATTRS,
+ (dce2CommonStats*)&dce2_smb_stats);
+ ssd->cur_rtracker->file_size = SmbOpenAndXReqAllocSize((SmbOpenAndXReq*)nb_ptr);
+ }
+
+ DCE2_MOVE(nb_ptr, nb_len, DCE2_ComInfoCommandSize(com_info));
+
+ if (unicode)
+ pad = (nb_ptr - (const uint8_t*)smb_hdr) & 1;
+
+ if (nb_len < (pad + null_bytes))
+ return DCE2_RET__ERROR;
+
+ DCE2_MOVE(nb_ptr, nb_len, pad);
+
+ // Samba allows chaining OpenAndX/NtCreateAndX so might have
+ // already been set.
+ if (ssd->cur_rtracker->file_name == nullptr)
+ {
+ ssd->cur_rtracker->file_name =
+ DCE2_SmbGetString(nb_ptr, nb_len, unicode, false);
+ }
+ }
+
+ return DCE2_RET__SUCCESS;
+}
+
+// SMB_COM_READ_ANDX
+static DCE2_Ret DCE2_SmbReadAndX(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
+ const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
+{
+ if (!DCE2_ComInfoCanProcessCommand(com_info))
+ return DCE2_RET__ERROR;
+
+ if (DCE2_ComInfoIsRequest(com_info))
+ {
+ DCE2_SmbFileTracker* ftracker =
+ DCE2_SmbGetFileTracker(ssd, SmbReadAndXReqFid((SmbReadAndXReq*)nb_ptr));
+
+ // No sense in tracking response
+ if (ftracker == nullptr)
+ return DCE2_RET__ERROR;
+
+ if (!ftracker->is_ipc)
+ ssd->cur_rtracker->file_offset = SmbReadAndXReqOffset((SmbReadAndXExtReq*)nb_ptr);
+
+ // Set this for response
+ ssd->cur_rtracker->ftracker = ftracker;
+ }
+ else
+ {
+ uint16_t com_size = DCE2_ComInfoCommandSize(com_info);
+ uint16_t byte_count = DCE2_ComInfoByteCount(com_info);
+ uint16_t doff = SmbReadAndXRespDataOff((SmbReadAndXResp*)nb_ptr);
+ uint32_t dcnt = SmbReadAndXRespDataCnt((SmbReadAndXResp*)nb_ptr);
+
+ DCE2_MOVE(nb_ptr, nb_len, com_size);
+
+ if (DCE2_SmbCheckData(ssd, (uint8_t*)smb_hdr, nb_ptr, nb_len,
+ byte_count, dcnt, doff) != DCE2_RET__SUCCESS)
+ return DCE2_RET__ERROR;
+
+ // This may move backwards
+ DCE2_MOVE(nb_ptr, nb_len, ((uint8_t*)smb_hdr + doff) - nb_ptr);
+
+ if (dcnt > nb_len)
+ dcnt = nb_len;
+
+ return DCE2_SmbProcessResponseData(ssd, nb_ptr, dcnt);
+ }
+
+ return DCE2_RET__SUCCESS;
+}
+
+// SMB_COM_WRITE_ANDX
+static DCE2_Ret DCE2_SmbWriteAndX(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
+ const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
+{
+ if (!DCE2_ComInfoCanProcessCommand(com_info))
+ {
+ DCE2_SmbFileTracker* ftracker = ssd->cur_rtracker->ftracker;
+
+ if ((ftracker != nullptr) && ftracker->is_ipc
+ && (ftracker->fp_writex_raw != nullptr))
+ {
+ ftracker->fp_writex_raw->remaining = 0;
+ DCE2_BufferEmpty(ftracker->fp_writex_raw->buf);
+ }
+
+ return DCE2_RET__ERROR;
+ }
+
+ if (DCE2_ComInfoIsRequest(com_info)
+ && (SmbWriteAndXReqStartRaw((SmbWriteAndXReq*)nb_ptr)
+ || SmbWriteAndXReqRaw((SmbWriteAndXReq*)nb_ptr)))
+ {
+ DCE2_SmbFileTracker* ftracker =
+ DCE2_SmbGetFileTracker(ssd, SmbWriteAndXReqFid((SmbWriteAndXReq*)nb_ptr));
+
+ // Raw mode is only applicable to named pipes.
+ if ((ftracker != nullptr) && ftracker->is_ipc)
+ return DCE2_SmbWriteAndXRawRequest(ssd, smb_hdr, com_info, nb_ptr, nb_len);
+ }
+
+ if (DCE2_ComInfoIsRequest(com_info))
+ {
+ uint16_t com_size = DCE2_ComInfoCommandSize(com_info);
+ uint16_t byte_count = DCE2_ComInfoByteCount(com_info);
+ uint16_t fid = SmbWriteAndXReqFid((SmbWriteAndXReq*)nb_ptr);
+ uint16_t doff = SmbWriteAndXReqDataOff((SmbWriteAndXReq*)nb_ptr);
+ uint32_t dcnt = SmbWriteAndXReqDataCnt((SmbWriteAndXReq*)nb_ptr);
+ uint64_t offset = SmbWriteAndXReqOffset((SmbWriteAndXExtReq*)nb_ptr);
+
+ DCE2_MOVE(nb_ptr, nb_len, com_size);
+
+ if (DCE2_SmbCheckData(ssd, (uint8_t*)smb_hdr, nb_ptr, nb_len,
+ byte_count, dcnt, doff) != DCE2_RET__SUCCESS)
+ return DCE2_RET__ERROR;
+
+ // This may move backwards
+ DCE2_MOVE(nb_ptr, nb_len, ((uint8_t*)smb_hdr + doff) - nb_ptr);
+
+ if (dcnt > nb_len)
+ {
+ // Current Samba errors if data count is greater than data left
+ if (DCE2_SsnGetPolicy(&ssd->sd) == DCE2_POLICY__SAMBA)
+ return DCE2_RET__ERROR;
+
+ // Windows and early Samba just use what's left
+ dcnt = nb_len;
+ }
+
+ return DCE2_SmbProcessRequestData(ssd, fid, nb_ptr, dcnt, offset);
+ }
+
+ return DCE2_RET__SUCCESS;
+}
+
+// SMB_COM_WRITE_ANDX - raw mode
+static DCE2_Ret DCE2_SmbWriteAndXRawRequest(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
+ const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
+{
+ DebugMessage(DEBUG_DCE_SMB,
+ "Processing WriteAndX with raw mode flags\n");
+
+ // Set this now for possible reassembled packet
+ uint16_t fid = SmbWriteAndXReqFid((SmbWriteAndXReq*)nb_ptr);
+ DCE2_SmbFileTracker* ftracker = DCE2_SmbGetFileTracker(ssd, fid);
+ ssd->cur_rtracker->ftracker = ftracker;
+ if (ftracker == nullptr)
+ return DCE2_RET__ERROR;
+
+ // Got request to write in raw mode without having gotten the initial
+ // raw mode request or got initial raw mode request and then another
+ // without having finished the first.
+ bool start_write_raw = SmbWriteAndXReqStartRaw((SmbWriteAndXReq*)nb_ptr);
+ bool continue_write_raw = SmbWriteAndXReqRaw((SmbWriteAndXReq*)nb_ptr);
+ DCE2_Policy policy = DCE2_SsnGetServerPolicy(&ssd->sd);
+ if ((start_write_raw && (ftracker->fp_writex_raw != nullptr)
+ && (ftracker->fp_writex_raw->remaining != 0))
+ || (continue_write_raw && ((ftracker->fp_writex_raw == nullptr)
+ || (ftracker->fp_writex_raw->remaining == 0))))
+ {
+ switch (policy)
+ {
+ case DCE2_POLICY__WIN2000:
+ case DCE2_POLICY__WINXP:
+ case DCE2_POLICY__WINVISTA:
+ case DCE2_POLICY__WIN2003:
+ case DCE2_POLICY__WIN2008:
+ case DCE2_POLICY__WIN7:
+ if (ftracker->fp_writex_raw != nullptr)
+ {
+ ftracker->fp_writex_raw->remaining = 0;
+ DCE2_BufferEmpty(ftracker->fp_writex_raw->buf);
+ }
+ return DCE2_RET__ERROR;
+ case DCE2_POLICY__SAMBA:
+ case DCE2_POLICY__SAMBA_3_0_37:
+ case DCE2_POLICY__SAMBA_3_0_22:
+ case DCE2_POLICY__SAMBA_3_0_20:
+ // Samba doesn't do anything special here except if the two
+ // flags are set it walks past the two "length" bytes.
+ // See below.
+ break;
+ default:
+ DebugFormat(DEBUG_DCE_SMB, "%s(%d) Invalid policy: %d",
+ __FILE__, __LINE__, policy);
+ break;
+ }
+ }
+
+ uint16_t com_size = DCE2_ComInfoCommandSize(com_info);
+ uint16_t byte_count = DCE2_ComInfoByteCount(com_info);
+ uint16_t doff = SmbWriteAndXReqDataOff((SmbWriteAndXReq*)nb_ptr);
+ uint32_t dcnt = SmbWriteAndXReqDataCnt((SmbWriteAndXReq*)nb_ptr);
+ uint16_t remaining = SmbWriteAndXReqRemaining((SmbWriteAndXReq*)nb_ptr);
+
+ DCE2_MOVE(nb_ptr, nb_len, com_size);
+
+ if (DCE2_SmbCheckData(ssd, (uint8_t*)smb_hdr, nb_ptr, nb_len,
+ byte_count, dcnt, doff) != DCE2_RET__SUCCESS)
+ return DCE2_RET__ERROR;
+
+ // This may move backwards
+ DCE2_MOVE(nb_ptr, nb_len, ((uint8_t*)smb_hdr + doff) - nb_ptr);
+
+ // If a "raw" write is requested there will be two bytes after the
+ // header/pad and before the data which is supposed to represent a
+ // length but everyone ignores it. However we need to move past it.
+ // This is the one situation where the remaining field matters and
+ // should be equal to the total amount of data to be written.
+ if (start_write_raw)
+ {
+ if (dcnt < 2)
+ return DCE2_RET__ERROR;
+
+ // From data size check above, nb_len >= dsize
+ dcnt -= 2;
+ DCE2_MOVE(nb_ptr, nb_len, 2);
+ }
+
+ if (dcnt > nb_len)
+ dcnt = nb_len;
+
+ // File tracker already validated
+ switch (policy)
+ {
+ case DCE2_POLICY__WIN2000:
+ case DCE2_POLICY__WINXP:
+ case DCE2_POLICY__WINVISTA:
+ case DCE2_POLICY__WIN2003:
+ case DCE2_POLICY__WIN2008:
+ case DCE2_POLICY__WIN7:
+ if (start_write_raw)
+ {
+ if (ftracker->fp_writex_raw == nullptr)
+ {
+ ftracker->fp_writex_raw = (DCE2_SmbWriteAndXRaw*)
+ SnortAlloc(sizeof(DCE2_SmbWriteAndXRaw));
+ if (ftracker->fp_writex_raw == nullptr)
+ return DCE2_RET__ERROR;
+
+ ftracker->fp_writex_raw->remaining = (int)remaining;
+ }
+ }
+
+ ftracker->fp_writex_raw->remaining -= (int)dcnt;
+ if (ftracker->fp_writex_raw->remaining < 0)
+ {
+ ftracker->fp_writex_raw->remaining = 0;
+ DCE2_BufferEmpty(ftracker->fp_writex_raw->buf);
+ return DCE2_RET__ERROR;
+ }
+
+ // If the "raw" write isn't finished in the first request
+ // and haven't allocated a buffer yet.
+ if (start_write_raw && (ftracker->fp_writex_raw->remaining != 0)
+ && (ftracker->fp_writex_raw->buf == nullptr))
+ {
+ ftracker->fp_writex_raw->buf =
+ DCE2_BufferNew(remaining, 0);
+ if (ftracker->fp_writex_raw->buf == nullptr)
+ {
+ ftracker->fp_writex_raw->remaining = 0;
+ return DCE2_RET__ERROR;
+ }
+ }
+
+ // If data has to be added to buffer, i.e. not a start raw
+ // or a start raw and more raw requests to come.
+ if (!start_write_raw || (ftracker->fp_writex_raw->remaining != 0))
+ {
+ if (DCE2_BufferAddData(ftracker->fp_writex_raw->buf, nb_ptr,
+ dcnt, DCE2_BufferLength(ftracker->fp_writex_raw->buf),
+ DCE2_BUFFER_MIN_ADD_FLAG__IGNORE) != DCE2_RET__SUCCESS)
+ {
+ ftracker->fp_writex_raw->remaining = 0;
+ DCE2_BufferEmpty(ftracker->fp_writex_raw->buf);
+ return DCE2_RET__ERROR;
+ }
+
+ if (ftracker->fp_writex_raw->remaining == 0)
+ {
+ //FIXIT-M - port Create reassembled packet
+/*
+ const uint8_t *data_ptr = DCE2_BufferData(ftracker->fp_writex_raw->buf);
+ uint32_t data_len = DCE2_BufferLength(ftracker->fp_writex_raw->buf);
+ SFSnortPacket *rpkt = DCE2_SmbGetRpkt(ssd,
+ &data_ptr, &data_len, DCE2_RPKT_TYPE__SMB_TRANS);
+
+ if (rpkt == nullptr)
{
- case SMB_ANDX_COM__SESSION_SETUP_ANDX:
- switch (com)
- {
- case SMB_COM_LOGOFF_ANDX:
- case SMB_COM_TREE_CONNECT:
- case SMB_COM_TREE_CONNECT_ANDX:
- case SMB_COM_TREE_DISCONNECT:
- case SMB_COM_OPEN_ANDX:
- case SMB_COM_NT_CREATE_ANDX:
- case SMB_COM_CLOSE:
- case SMB_COM_READ_ANDX:
- com_func = smb_com_funcs[com];
- break;
- case SMB_COM_WRITE:
- if ((policy == DCE2_POLICY__SAMBA_3_0_22)
- || (policy == DCE2_POLICY__SAMBA_3_0_20))
- com_func = smb_com_funcs[com];
- break;
- default:
- break;
- }
- break;
- case SMB_ANDX_COM__LOGOFF_ANDX:
- switch (com)
- {
- case SMB_COM_SESSION_SETUP_ANDX:
- case SMB_COM_TREE_DISCONNECT:
- com_func = smb_com_funcs[com];
- break;
- default:
- break;
- }
- break;
- case SMB_ANDX_COM__TREE_CONNECT_ANDX:
- switch (com)
- {
- case SMB_COM_SESSION_SETUP_ANDX:
- case SMB_COM_LOGOFF_ANDX:
- case SMB_COM_TREE_DISCONNECT:
- case SMB_COM_OPEN_ANDX:
- case SMB_COM_NT_CREATE_ANDX:
- case SMB_COM_CLOSE:
- case SMB_COM_WRITE:
- case SMB_COM_READ_ANDX:
- com_func = smb_com_funcs[com];
- break;
- default:
- break;
- }
- break;
- case SMB_ANDX_COM__OPEN_ANDX:
- switch (com)
- {
- case SMB_COM_SESSION_SETUP_ANDX:
- case SMB_COM_LOGOFF_ANDX:
- case SMB_COM_TREE_CONNECT:
- case SMB_COM_TREE_CONNECT_ANDX:
- case SMB_COM_TREE_DISCONNECT:
- case SMB_COM_OPEN_ANDX:
- case SMB_COM_NT_CREATE_ANDX:
- case SMB_COM_CLOSE:
- case SMB_COM_WRITE:
- case SMB_COM_READ_ANDX:
- com_func = smb_com_funcs[com];
- break;
- default:
- break;
- }
- break;
- case SMB_ANDX_COM__NT_CREATE_ANDX:
- switch (com)
- {
- case SMB_COM_SESSION_SETUP_ANDX:
- case SMB_COM_TREE_CONNECT:
- case SMB_COM_TREE_CONNECT_ANDX:
- case SMB_COM_OPEN_ANDX:
- case SMB_COM_NT_CREATE_ANDX:
- case SMB_COM_WRITE:
- case SMB_COM_READ_ANDX:
- com_func = smb_com_funcs[com];
- break;
- case SMB_COM_LOGOFF_ANDX:
- case SMB_COM_TREE_DISCONNECT:
- case SMB_COM_CLOSE:
- if ((policy == DCE2_POLICY__SAMBA)
- || (policy == DCE2_POLICY__SAMBA_3_0_37))
- com_func = smb_com_funcs[com];
- break;
- default:
- break;
- }
- break;
- case SMB_ANDX_COM__WRITE_ANDX:
- switch (com)
- {
- case SMB_COM_SESSION_SETUP_ANDX:
- case SMB_COM_LOGOFF_ANDX:
- case SMB_COM_TREE_CONNECT:
- case SMB_COM_TREE_CONNECT_ANDX:
- case SMB_COM_OPEN_ANDX:
- case SMB_COM_NT_CREATE_ANDX:
- case SMB_COM_CLOSE:
- case SMB_COM_WRITE:
- case SMB_COM_READ_ANDX:
- case SMB_COM_WRITE_ANDX:
- com_func = smb_com_funcs[com];
- break;
- default:
- break;
- }
- break;
- case SMB_ANDX_COM__READ_ANDX:
- switch (com)
- {
- case SMB_COM_SESSION_SETUP_ANDX:
- case SMB_COM_WRITE:
- com_func = smb_com_funcs[com];
- break;
- case SMB_COM_LOGOFF_ANDX:
- case SMB_COM_TREE_CONNECT:
- case SMB_COM_TREE_CONNECT_ANDX:
- case SMB_COM_TREE_DISCONNECT:
- case SMB_COM_OPEN_ANDX:
- case SMB_COM_NT_CREATE_ANDX:
- case SMB_COM_CLOSE:
- case SMB_COM_READ_ANDX:
- if ((policy == DCE2_POLICY__SAMBA)
- || (policy == DCE2_POLICY__SAMBA_3_0_37))
- com_func = smb_com_funcs[com];
- break;
- default:
- break;
- }
- break;
- default:
- break;
+ DCE2_BufferEmpty(ftracker->fp_writex_raw->buf);
+ return DCE2_RET__ERROR;
}
+
+ DebugMessage(DEBUG_DCE_SMB,
+ "Reassembled WriteAndX raw mode request\n"));
+ DCE2_DEBUG_CODE(DCE2_DEBUG__MAIN, DCE2_PrintPktData(rpkt->payload, rpkt->payload_size););
+
+ (void)DCE2_SmbProcessRequestData(ssd, fid, data_ptr, data_len, 0);
+
+ DCE2_SmbReturnRpkt();
+ DCE2_BufferEmpty(ftracker->fp_writex_raw->buf);
+*/
+ }
+ }
+ else
+ {
+ (void)DCE2_SmbProcessRequestData(ssd, fid, nb_ptr, dcnt, 0);
+ }
+
+ // Windows doesn't process chained commands to raw WriteAndXs
+ // so return error so it exits the loop.
+ return DCE2_RET__ERROR;
+
+ case DCE2_POLICY__SAMBA:
+ case DCE2_POLICY__SAMBA_3_0_37:
+ case DCE2_POLICY__SAMBA_3_0_22:
+ case DCE2_POLICY__SAMBA_3_0_20:
+ // All Samba cares about is skipping the 2 byte "length"
+ // if both flags are set.
+ break;
+ default:
+ DebugFormat(DEBUG_DCE_SMB, "%s(%d) Invalid policy: %d",
+ __FILE__, __LINE__, policy);
+ break;
+ }
+
+ return DCE2_SmbProcessRequestData(ssd, fid, nb_ptr, dcnt, 0);
+}
+
+// SMB_COM_SESSION_SETUP_ANDX
+static DCE2_Ret DCE2_SmbSessionSetupAndX(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
+ const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t)
+{
+ if (!DCE2_ComInfoCanProcessCommand(com_info))
+ return DCE2_RET__ERROR;
+
+ if (DCE2_ComInfoIsRequest(com_info))
+ {
+ uint16_t max_multiplex =
+ SmbSessionSetupAndXReqMaxMultiplex((SmbLm10_SessionSetupAndXReq*)nb_ptr);
+
+ if (max_multiplex < ssd->max_outstanding_requests)
+ ssd->max_outstanding_requests = max_multiplex;
+
+ // FIXIT-M port fingerprint related code
+ }
+ else
+ {
+ uint16_t uid = SmbUid(smb_hdr);
+
+ DCE2_SmbInsertUid(ssd, uid);
+ ssd->cur_rtracker->uid = uid; // Set this in case there are chained commands
+
+ if (!(ssd->ssn_state_flags & DCE2_SMB_SSN_STATE__NEGOTIATED))
+ ssd->ssn_state_flags |= DCE2_SMB_SSN_STATE__NEGOTIATED;
+
+ // FIXIT-M port fingerprint related code
+ }
+
+ return DCE2_RET__SUCCESS;
+}
+
+// SMB_COM_NEGOTIATE
+static DCE2_Ret DCE2_SmbNegotiate(DCE2_SmbSsnData* ssd, const SmbNtHdr*,
+ const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t)
+{
+ if (!DCE2_ComInfoCanProcessCommand(com_info))
+ return DCE2_RET__ERROR;
+
+ Profile profile(dce2_smb_pstat_smb_negotiate);
+
+ if (DCE2_ComInfoIsRequest(com_info))
+ {
+ // FIXIT-M add dialect related code
+ }
+ else
+ {
+ // FIXIT-M add dialect related code
+
+ ssd->ssn_state_flags |= DCE2_SMB_SSN_STATE__NEGOTIATED;
+
+ if (DCE2_ComInfoWordCount(com_info) == 17)
+ {
+ ssd->max_outstanding_requests =
+ SmbNt_NegotiateRespMaxMultiplex((SmbNt_NegotiateProtocolResp*)nb_ptr);
+ }
+ else if (DCE2_ComInfoWordCount(com_info) == 13)
+ {
+ ssd->max_outstanding_requests =
+ SmbLm_NegotiateRespMaxMultiplex((SmbLm10_NegotiateProtocolResp*)nb_ptr);
+ }
+ else
+ {
+ ssd->max_outstanding_requests = 1;
+ }
+ }
+
+ return DCE2_RET__SUCCESS;
+}
+
+// SMB_COM_TREE_CONNECT_ANDX
+
+#define SERVICE_0 (0) // IPC start
+#define SERVICE_1 (SERVICE_0+4) // DISK start
+#define SERVICE_FS (SERVICE_1+3) // Failure
+#define SERVICE_IPC (SERVICE_FS+1) // IPC service
+#define SERVICE_DISK (SERVICE_FS+2) // DISK service
+
+static const DCE2_SmbFsm dce2_smb_service_fsm[] =
+{
+ // IPC
+ { 'I', SERVICE_0+1, SERVICE_1 },
+ { 'P', SERVICE_0+2, SERVICE_FS },
+ { 'C', SERVICE_0+3, SERVICE_FS },
+ { '\0', SERVICE_IPC, SERVICE_FS },
+
+ // DISK
+ { 'A', SERVICE_1+1, SERVICE_FS },
+ { ':', SERVICE_1+2, SERVICE_FS },
+ { '\0', SERVICE_DISK, SERVICE_FS },
+
+ { 0, SERVICE_FS, SERVICE_FS }
+};
+
+static DCE2_Ret DCE2_SmbTreeConnectAndX(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
+ const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
+{
+ uint16_t com_size = DCE2_ComInfoCommandSize(com_info);
+
+ if (!DCE2_ComInfoCanProcessCommand(com_info))
+ return DCE2_RET__ERROR;
+
+ if (DCE2_ComInfoIsRequest(com_info))
+ {
+ if (DCE2_ScSmbInvalidShares((dce2SmbProtoConf*)ssd->sd.config) != nullptr)
+ {
+ uint16_t pass_len = SmbTreeConnectAndXReqPassLen((SmbTreeConnectAndXReq*)nb_ptr);
+ DCE2_MOVE(nb_ptr, nb_len, com_size);
+ if (pass_len >= nb_len)
+ return DCE2_RET__ERROR;
+
+ // Move past password length
+ DCE2_MOVE(nb_ptr, nb_len, pass_len);
+
+ const uint8_t* bs = nullptr;
+ // Move past path components
+ while ((bs = (const uint8_t*)memchr(nb_ptr, '\\', nb_len)) != nullptr)
+ DCE2_MOVE(nb_ptr, nb_len, (bs - nb_ptr) + 1);
+
+ // Move past NULL byte if unicode
+ if (SmbUnicode(smb_hdr) && (nb_len != 0))
+ DCE2_MOVE(nb_ptr, nb_len, 1);
+
+ if (nb_len != 0)
+ DCE2_SmbInvalidShareCheck(ssd, smb_hdr, nb_ptr, nb_len);
+ }
+ }
+ else
+ {
+ DCE2_MOVE(nb_ptr, nb_len, com_size);
+
+ int state = SERVICE_0;
+ while ((nb_len > 0) && (state < SERVICE_FS))
+ {
+ if (dce2_smb_service_fsm[state].input == (char)*nb_ptr)
+ {
+ state = dce2_smb_service_fsm[state].next_state;
+ DCE2_MOVE(nb_ptr, nb_len, 1);
+ }
+ else
+ {
+ state = dce2_smb_service_fsm[state].fail_state;
+ }
+ }
+
+ uint16_t tid = SmbTid(smb_hdr);
+ bool is_ipc = true;
+ switch (state)
+ {
+ case SERVICE_IPC:
+ DebugFormat(DEBUG_DCE_SMB,
+ "Tid (%u) is an IPC tree.\n", tid);
+ break;
+ case SERVICE_DISK:
+ is_ipc = false;
+ DebugFormat(DEBUG_DCE_SMB,
+ "Tid (%u) is a DISK tree.\n", tid);
+ break;
+ default:
+ return DCE2_RET__IGNORE;
+ }
+
+ // Insert tid into list
+ DCE2_SmbInsertTid(ssd, tid, is_ipc);
+ ssd->cur_rtracker->tid = tid; // Set this in case there are chained commands
+ }
+
+ return DCE2_RET__SUCCESS;
+}
+
+#define SHARE_0 (0)
+#define SHARE_FS (SHARE_0+5)
+#define SHARE_IPC (SHARE_FS+1)
+
+static const DCE2_SmbFsm dce2_ipc_share_fsm[] =
+{
+ { 'I', SHARE_0+1, SHARE_FS },
+ { 'P', SHARE_0+2, SHARE_FS },
+ { 'C', SHARE_0+3, SHARE_FS },
+ { '$', SHARE_0+4, SHARE_FS },
+ { '\0', SHARE_IPC, SHARE_FS },
+
+ { 0, SHARE_FS, SHARE_FS }
+};
+
+// SMB_COM_TREE_CONNECT
+static DCE2_Ret DCE2_SmbTreeConnect(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
+ const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
+{
+ if (!DCE2_ComInfoCanProcessCommand(com_info))
+ return DCE2_RET__ERROR;
+
+ if (DCE2_ComInfoIsRequest(com_info))
+ {
+ uint16_t com_size = DCE2_ComInfoCommandSize(com_info);
+
+ // Have at least 4 bytes of data based on byte count check done earlier
+
+ DCE2_MOVE(nb_ptr, nb_len, com_size);
+
+ // If unicode flag is set, strings, except possibly the service string
+ // are going to be unicode. The NT spec specifies that unicode strings
+ // must be word aligned with respect to the beginning of the SMB and that for
+ // type-prefixed strings (this case), the padding byte is found after the
+ // type format byte.
+
+ // This byte will realign things.
+ if (*nb_ptr != SMB_FMT__ASCII)
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_BAD_FORM, (dce2CommonStats*)&dce2_smb_stats);
+ return DCE2_RET__ERROR;
+ }
+
+ DCE2_MOVE(nb_ptr, nb_len, 1);
+
+ // IPC$ does not need to be case sensitive. And the case sensitivity flag in
+ // the SMB header doesn't seem to have any effect on this.
+ const uint8_t* bs = nullptr;
+ while ((bs = (const uint8_t*)memchr(nb_ptr, '\\', nb_len)) != nullptr)
+ DCE2_MOVE(nb_ptr, nb_len, (bs - nb_ptr) + 1);
+
+ bool unicode = SmbUnicode(smb_hdr);
+ if (unicode && (nb_len > 0))
+ DCE2_MOVE(nb_ptr, nb_len, 1);
+
+ // Check for invalid shares first
+ if ((DCE2_ScSmbInvalidShares((dce2SmbProtoConf*)ssd->sd.config) != nullptr) && (nb_len >
+ 0))
+ DCE2_SmbInvalidShareCheck(ssd, smb_hdr, nb_ptr, nb_len);
+
+ int state = SHARE_0;
+ uint8_t increment = unicode ? 2 : 1;
+ while ((nb_len >= increment) && (state < SHARE_FS))
+ {
+ if (dce2_ipc_share_fsm[state].input == toupper((int)nb_ptr[0]))
+ {
+ if (unicode && (nb_ptr[1] != 0))
break;
- default:
- break;
- }
+ state = dce2_ipc_share_fsm[state].next_state;
+ DCE2_MOVE(nb_ptr, nb_len, increment);
+ }
+ else
+ {
+ state = dce2_ipc_share_fsm[state].fail_state;
+ }
+ }
+
+ bool is_ipc = false;
+ switch (state)
+ {
+ case SHARE_IPC:
+ is_ipc = true;
+ break;
+ case SHARE_FS:
+ default:
+ break;
+ }
+
+ ssd->cur_rtracker->is_ipc = is_ipc;
+ }
+ else
+ {
+ // FIXIT-L What if the TID in the SMB header differs from that returned
+ // in the TreeConnect command response?
+ uint16_t tid = SmbTid(smb_hdr);
+ DCE2_SmbInsertTid(ssd, tid, ssd->cur_rtracker->is_ipc);
+
+ DebugFormat(DEBUG_DCE_SMB, "Tid (%u) %s an IPC tree\n", tid,
+ (ssd->cur_rtracker->is_ipc) ? "is" : "is not");
+ }
+
+ return DCE2_RET__SUCCESS;
+}
+
+// SMB_COM_NT_CREATE_ANDX
+static DCE2_Ret DCE2_SmbNtCreateAndX(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
+ const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
+{
+ if (!DCE2_ComInfoCanProcessCommand(com_info))
+ return DCE2_RET__ERROR;
+
+ if (DCE2_ComInfoIsResponse(com_info))
+ {
+ const uint16_t fid = SmbNtCreateAndXRespFid((SmbNtCreateAndXResp*)nb_ptr);
+ DCE2_SmbFileTracker* ftracker = nullptr;
+
+ // Set request tracker's current file tracker in case of chained commands
+ switch (SmbAndXCom2((SmbAndXCommon*)nb_ptr))
+ {
+ // This is in case in the request a write was chained to an open
+ // in which case the write will be to the newly opened file
+ case SMB_COM_WRITE:
+ case SMB_COM_WRITE_ANDX:
+ case SMB_COM_TRANSACTION:
+ case SMB_COM_READ_ANDX:
+ ftracker = DCE2_SmbDequeueTmpFileTracker(ssd, ssd->cur_rtracker, fid);
+ break;
+ default:
+ break;
+ }
+
+ if (!DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid))
+ {
+ const bool is_directory = SmbNtCreateAndXRespDirectory((SmbNtCreateAndXResp*)nb_ptr);
+ const uint16_t resource_type =
+ SmbNtCreateAndXRespResourceType((SmbNtCreateAndXResp*)nb_ptr);
+
+ if (is_directory || !SmbResourceTypeDisk(resource_type))
+ {
+ if (ftracker != nullptr)
+ DCE2_SmbRemoveFileTracker(ssd, ftracker);
+ return DCE2_RET__SUCCESS;
+ }
+
+ // Give preference to files opened with the sequential only flag set
+// FIXIT-M uncomment once fileApi is ported
+/*
+ if (((ssd->fapi_ftracker == nullptr) || !ssd->fapi_ftracker->ff_sequential_only)
+ && (ftracker == nullptr) && ssd->cur_rtracker->sequential_only)
+ {
+ DCE2_SmbAbortFileAPI(ssd);
+ }
+*/
+ }
+
+ if (ftracker == nullptr)
+ {
+ ftracker = DCE2_SmbNewFileTracker(ssd,
+ ssd->cur_rtracker->uid, ssd->cur_rtracker->tid, fid);
+ if (ftracker == nullptr)
+ return DCE2_RET__ERROR;
+ }
+
+ ftracker->file_name = ssd->cur_rtracker->file_name;
+ ssd->cur_rtracker->file_name = nullptr;
+
+ DebugFormat(DEBUG_DCE_SMB, "File name: %s\n",
+ (ftracker->file_name == nullptr) ? "nullptr" : ftracker->file_name);
+
+ if (!ftracker->is_ipc)
+ {
+ const uint32_t create_disposition =
+ SmbNtCreateAndXRespCreateDisposition((SmbNtCreateAndXResp*)nb_ptr);
+
+ if (SmbCreateDispositionRead(create_disposition))
+ {
+ ftracker->ff_file_size =
+ SmbNtCreateAndXRespEndOfFile((SmbNtCreateAndXResp*)nb_ptr);
+ }
+ else
+ {
+ ftracker->ff_file_size = ssd->cur_rtracker->file_size;
+ ftracker->ff_file_direction = DCE2_SMB_FILE_DIRECTION__UPLOAD;
+ }
+
+ ftracker->ff_sequential_only = ssd->cur_rtracker->sequential_only;
+ }
+
+ ssd->cur_rtracker->ftracker = ftracker;
+ }
+ else
+ {
+ bool is_ipc = DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid);
+ uint8_t smb_com2 = SmbAndXCom2((SmbAndXCommon*)nb_ptr);
+ uint16_t file_name_length =
+ SmbNtCreateAndXReqFileNameLen((SmbNtCreateAndXReq*)nb_ptr);
+
+ if (!is_ipc)
+ {
+ uint32_t ext_file_attrs =
+ SmbNtCreateAndXReqFileAttrs((SmbNtCreateAndXReq*)nb_ptr);
+
+ if (SmbEvasiveFileAttrs(ext_file_attrs))
+ dce_alert(GID_DCE2, DCE2_SMB_EVASIVE_FILE_ATTRS,
+ (dce2CommonStats*)&dce2_smb_stats);
+ // If the file is going to be accessed sequentially, track it.
+ if (SmbNtCreateAndXReqSequentialOnly((SmbNtCreateAndXReq*)nb_ptr))
+ ssd->cur_rtracker->sequential_only = true;
+
+ ssd->cur_rtracker->file_size = SmbNtCreateAndXReqAllocSize(
+ (SmbNtCreateAndXReq*)nb_ptr);
+ }
+
+ DCE2_MOVE(nb_ptr, nb_len, DCE2_ComInfoCommandSize(com_info));
+
+ if (file_name_length > DCE2_SMB_MAX_PATH_LEN)
+ return DCE2_RET__ERROR;
+
+ uint32_t pad = 0;
+ const bool unicode = SmbUnicode(smb_hdr);
+ if (unicode)
+ pad = (nb_ptr - (const uint8_t*)smb_hdr) & 1;
+
+ if (nb_len < (pad + file_name_length))
+ return DCE2_RET__ERROR;
+
+ DCE2_MOVE(nb_ptr, nb_len, pad);
+
+ // Samba allows chaining OpenAndX/NtCreateAndX so might have
+ // already been set.
+ if (ssd->cur_rtracker->file_name == nullptr)
+ {
+ ssd->cur_rtracker->file_name =
+ DCE2_SmbGetString(nb_ptr, file_name_length, unicode, false);
+ }
+
+ if (is_ipc)
+ {
+ switch (smb_com2)
+ {
+ case SMB_COM_READ_ANDX:
+ if (DCE2_SsnIsWindowsPolicy(&ssd->sd))
+ return DCE2_RET__ERROR;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ return DCE2_RET__SUCCESS;
+}
+
+// SMB_COM_TREE_DISCONNECT
+static DCE2_Ret DCE2_SmbTreeDisconnect(DCE2_SmbSsnData* ssd, const SmbNtHdr*,
+ const DCE2_SmbComInfo* com_info, const uint8_t*, uint32_t)
+{
+ if (!DCE2_ComInfoCanProcessCommand(com_info))
+ return DCE2_RET__ERROR;
+
+ if (DCE2_ComInfoIsResponse(com_info))
+ DCE2_SmbRemoveTid(ssd, ssd->cur_rtracker->tid);
+
+ return DCE2_RET__SUCCESS;
+}
+
+// SMB_COM_LOGOFF_ANDX
+static DCE2_Ret DCE2_SmbLogoffAndX(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
+ const DCE2_SmbComInfo* com_info, const uint8_t*, uint32_t)
+{
+ if (!DCE2_ComInfoCanProcessCommand(com_info))
+ return DCE2_RET__ERROR;
+
+ if (DCE2_ComInfoIsResponse(com_info))
+ {
+ DCE2_SmbRemoveUid(ssd, ssd->cur_rtracker->uid);
+
+ switch (DCE2_SsnGetServerPolicy(&ssd->sd))
+ {
+ case DCE2_POLICY__WIN2000:
+ case DCE2_POLICY__WINXP:
+ case DCE2_POLICY__WINVISTA:
+ case DCE2_POLICY__WIN2003:
+ case DCE2_POLICY__WIN2008:
+ case DCE2_POLICY__WIN7:
+ /* Windows responds to a chained LogoffAndX => SessionSetupAndX with a
+ * word count 3 LogoffAndX without the chained SessionSetupAndX */
+ if (DCE2_ComInfoWordCount(com_info) == 3)
+ {
+ uint16_t uid = SmbUid(smb_hdr);
+ DCE2_SmbInsertUid(ssd, uid);
+ ssd->cur_rtracker->uid = uid; // Set this in case there are chained commands
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return DCE2_RET__SUCCESS;
+}
+
+// SMB_COM_TRANSACTION Request
+static DCE2_Ret DCE2_SmbTransactionReq(DCE2_SmbSsnData* ssd,
+ DCE2_SmbTransactionTracker* ttracker,
+ const uint8_t* data_ptr, uint32_t data_len,
+ const uint8_t* param_ptr, uint32_t param_len)
+{
+ switch (ttracker->subcom)
+ {
+ case TRANS_TRANSACT_NMPIPE:
+ case TRANS_WRITE_NMPIPE:
+ if (DCE2_SmbProcessRequestData(ssd, 0,
+ data_ptr, data_len, 0) != DCE2_RET__SUCCESS)
+ return DCE2_RET__ERROR;
+ break;
+
+ case TRANS_SET_NMPIPE_STATE:
+ // Only two parameters but more seems okay
+ if (param_len >= 2)
+ {
+ if ((alignedNtohs((uint16_t*)param_ptr) & PIPE_STATE_MESSAGE_MODE))
+ ttracker->pipe_byte_mode = false;
+ else
+ ttracker->pipe_byte_mode = true;
+
+ // Won't get a response
+ if (DCE2_SsnIsWindowsPolicy(&ssd->sd) && ttracker->one_way)
+ {
+ DebugFormat(DEBUG_DCE_SMB,
+ "Setting pipe to %s mode\n",
+ ttracker->pipe_byte_mode ? "byte" : "message");
+
+ ssd->cur_rtracker->ftracker->fp_byte_mode = ttracker->pipe_byte_mode;
+ }
+ }
+ break;
+
+ case TRANS_READ_NMPIPE:
+ break;
+
+ default:
+ return DCE2_RET__IGNORE;
+ }
+
+ if (DCE2_SsnIsWindowsPolicy(&ssd->sd) && ttracker->one_way && ttracker->disconnect_tid)
+ DCE2_SmbRemoveTid(ssd, ssd->cur_rtracker->tid);
+
+ return DCE2_RET__SUCCESS;
+}
+
+// SMB_COM_TRANSACTION
+
+#define TRANS_NM_PIPE_0 (0)
+#define TRANS_NM_PIPE_1 (TRANS_NM_PIPE_0+7)
+#define TRANS_NM_PIPE_2 (TRANS_NM_PIPE_1+1)
+#define TRANS_NM_PIPE_3 (TRANS_NM_PIPE_2+1)
+#define TRANS_NM_PIPE_4 (TRANS_NM_PIPE_3+5)
+#define TRANS_NM_PIPE_5 (TRANS_NM_PIPE_4+5)
+#define TRANS_NM_PIPE_6 (TRANS_NM_PIPE_5+1)
+#define TRANS_NM_PIPE_7 (TRANS_NM_PIPE_6+5)
+#define TRANS_NM_PIPE_8 (TRANS_NM_PIPE_7+3)
+#define TRANS_NM_PIPE_9 (TRANS_NM_PIPE_8+6)
+#define TRANS_NM_PIPE_FS (TRANS_NM_PIPE_9+1)
+#define TRANS_NM_PIPE_DONE (TRANS_NM_PIPE_FS+1)
+
+static const DCE2_SmbFsm dce2_samba_pipe_fsm[] =
+{
+ // Normal sequence
+ { '\\', TRANS_NM_PIPE_0+1, TRANS_NM_PIPE_FS },
+ { 'P', TRANS_NM_PIPE_0+2, TRANS_NM_PIPE_FS },
+ { 'I', TRANS_NM_PIPE_0+3, TRANS_NM_PIPE_FS },
+ { 'P', TRANS_NM_PIPE_0+4, TRANS_NM_PIPE_FS },
+ { 'E', TRANS_NM_PIPE_0+5, TRANS_NM_PIPE_FS },
+ { '\\', TRANS_NM_PIPE_0+6, TRANS_NM_PIPE_1 },
+ { '\0', TRANS_NM_PIPE_DONE, TRANS_NM_PIPE_2 },
+
+ // Win98
+ { '\0', TRANS_NM_PIPE_DONE, TRANS_NM_PIPE_FS },
+
+ { 'W', TRANS_NM_PIPE_2+1, TRANS_NM_PIPE_5 },
+
+ { 'K', TRANS_NM_PIPE_3+1, TRANS_NM_PIPE_4 },
+ { 'S', TRANS_NM_PIPE_3+2, TRANS_NM_PIPE_FS },
+ { 'S', TRANS_NM_PIPE_3+3, TRANS_NM_PIPE_FS },
+ { 'V', TRANS_NM_PIPE_3+4, TRANS_NM_PIPE_FS },
+ { 'C', TRANS_NM_PIPE_9, TRANS_NM_PIPE_FS },
+
+ { 'I', TRANS_NM_PIPE_4+1, TRANS_NM_PIPE_FS },
+ { 'N', TRANS_NM_PIPE_4+2, TRANS_NM_PIPE_FS },
+ { 'R', TRANS_NM_PIPE_4+3, TRANS_NM_PIPE_FS },
+ { 'E', TRANS_NM_PIPE_4+4, TRANS_NM_PIPE_FS },
+ { 'G', TRANS_NM_PIPE_9, TRANS_NM_PIPE_FS },
+
+ { 'S', TRANS_NM_PIPE_5+1, TRANS_NM_PIPE_8 },
+
+ { 'R', TRANS_NM_PIPE_6+1, TRANS_NM_PIPE_5 },
+ { 'V', TRANS_NM_PIPE_6+2, TRANS_NM_PIPE_FS },
+ { 'S', TRANS_NM_PIPE_6+3, TRANS_NM_PIPE_FS },
+ { 'V', TRANS_NM_PIPE_6+4, TRANS_NM_PIPE_FS },
+ { 'C', TRANS_NM_PIPE_9, TRANS_NM_PIPE_FS },
+
+ { 'A', TRANS_NM_PIPE_7+1, TRANS_NM_PIPE_FS },
+ { 'M', TRANS_NM_PIPE_7+2, TRANS_NM_PIPE_FS },
+ { 'R', TRANS_NM_PIPE_9, TRANS_NM_PIPE_FS },
+
+ { 'L', TRANS_NM_PIPE_8+1, TRANS_NM_PIPE_FS },
+ { 'S', TRANS_NM_PIPE_8+2, TRANS_NM_PIPE_FS },
+ { 'A', TRANS_NM_PIPE_8+3, TRANS_NM_PIPE_FS },
+ { 'R', TRANS_NM_PIPE_8+4, TRANS_NM_PIPE_FS },
+ { 'P', TRANS_NM_PIPE_8+5, TRANS_NM_PIPE_FS },
+ { 'C', TRANS_NM_PIPE_9, TRANS_NM_PIPE_FS },
+
+ { '\0', TRANS_NM_PIPE_DONE, TRANS_NM_PIPE_FS },
+
+ { 0, TRANS_NM_PIPE_FS, TRANS_NM_PIPE_FS }
+};
+
+// Validates Name for Samba Transaction requests
+static DCE2_Ret DCE2_SmbTransactionGetName(const uint8_t* nb_ptr,
+ uint32_t nb_len, uint16_t bcc, bool unicode)
+{
+ if ((nb_len == 0) || (bcc == 0))
+ return DCE2_RET__ERROR;
+
+ if (bcc < nb_len)
+ nb_len = bcc;
+
+ if (unicode)
+ DCE2_MOVE(nb_ptr, nb_len, 1); // One byte pad for unicode
+
+ uint8_t increment = unicode ? 2 : 1;
+ int state = TRANS_NM_PIPE_0;
+ while ((nb_len >= increment) && (state < TRANS_NM_PIPE_FS))
+ {
+ if (dce2_samba_pipe_fsm[state].input == toupper((int)nb_ptr[0]))
+ {
+ if (unicode && (nb_ptr[1] != 0))
+ break;
+ state = dce2_samba_pipe_fsm[state].next_state;
+ DCE2_MOVE(nb_ptr, nb_len, increment);
+ }
+ else
+ {
+ state = dce2_samba_pipe_fsm[state].fail_state;
+ }
+ }
+
+ switch (state)
+ {
+ case TRANS_NM_PIPE_DONE:
+ break;
+ case TRANS_NM_PIPE_FS:
+ default:
+ return DCE2_RET__ERROR;
+ }
+
+ return DCE2_RET__SUCCESS;
+}
+
+static DCE2_Ret DCE2_SmbTransaction(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
+ const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
+{
+ uint16_t com_size = DCE2_ComInfoCommandSize(com_info);
+ DCE2_SmbTransactionTracker* ttracker = &ssd->cur_rtracker->ttracker;
+
+ // Got a matching request for an in progress transaction - don't process it,
+ // but don't want to remove tracker.
+ if (DCE2_ComInfoIsRequest(com_info)
+ && !DCE2_SmbIsTransactionComplete(ttracker))
+ {
+ DebugMessage(DEBUG_DCE_SMB, "Got new transaction request "
+ "that matches an in progress transaction - not inspecting.\n");
+ return DCE2_RET__ERROR;
+ }
+
+ // Avoid decoding/tracking \PIPE\LANMAN requests
+ if (DCE2_ComInfoIsRequest(com_info)
+ && (DCE2_ComInfoWordCount(com_info) != 16))
+ {
+ DebugMessage(DEBUG_DCE_SMB, "\\PIPE\\LANMAN request - not inspecting\n");
+ return DCE2_RET__IGNORE;
+ }
+
+ if (!DCE2_ComInfoCanProcessCommand(com_info))
+ return DCE2_RET__ERROR;
+
+ // Interim response is sent if client didn't send all data / parameters
+ // in initial Transaction request and will have to complete the request
+ // with TransactionSecondary commands.
+ if (DCE2_ComInfoIsResponse(com_info)
+ && (com_size == sizeof(SmbTransactionInterimResp)))
+ {
+ DebugMessage(DEBUG_DCE_SMB,
+ " Server Transaction interim response.\n");
+
+ return DCE2_RET__SUCCESS;
+ }
+
+ if (DCE2_ComInfoIsRequest(com_info))
+ {
+ DCE2_Ret status =
+ DCE2_SmbUpdateTransRequest(ssd, smb_hdr, com_info, nb_ptr, nb_len);
+
+ if (status != DCE2_RET__FULL)
+ return status;
+
+ ttracker->disconnect_tid = SmbTransactionReqDisconnectTid((SmbTransactionReq*)nb_ptr);
+ ttracker->one_way = SmbTransactionReqOneWay((SmbTransactionReq*)nb_ptr);
+
+ uint16_t doff = SmbTransactionReqDataOff((SmbTransactionReq*)nb_ptr);
+ uint16_t dcnt = SmbTransactionReqDataCnt((SmbTransactionReq*)nb_ptr);
+ uint16_t pcnt = SmbTransactionReqParamCnt((SmbTransactionReq*)nb_ptr);
+ uint16_t poff = SmbTransactionReqParamOff((SmbTransactionReq*)nb_ptr);
+
+ DCE2_MOVE(nb_ptr, nb_len, ((uint8_t*)smb_hdr + doff) - nb_ptr);
+ const uint8_t* data_ptr = nb_ptr;
- smb_chain_funcs[policy][andx][com] = com_func;
+ DCE2_MOVE(nb_ptr, nb_len, ((uint8_t*)smb_hdr + poff) - nb_ptr);
+ const uint8_t* param_ptr = nb_ptr;
+
+ status = DCE2_SmbTransactionReq(ssd, ttracker, data_ptr, dcnt, param_ptr, pcnt);
+ if (status != DCE2_RET__SUCCESS)
+ return status;
+ }
+ else
+ {
+ DCE2_Ret status =
+ DCE2_SmbUpdateTransResponse(ssd, smb_hdr, com_info, nb_ptr, nb_len);
+
+ if (status != DCE2_RET__FULL)
+ return status;
+
+ switch (ttracker->subcom)
+ {
+ case TRANS_TRANSACT_NMPIPE:
+ case TRANS_READ_NMPIPE:
+ {
+ // FIXIT-M port reassembly case
+
+ uint16_t dcnt = SmbTransactionRespDataCnt((SmbTransactionResp*)nb_ptr);
+ uint16_t doff = SmbTransactionRespDataOff((SmbTransactionResp*)nb_ptr);
+
+ DCE2_MOVE(nb_ptr, nb_len, ((uint8_t*)smb_hdr + doff) - nb_ptr);
+
+ if (DCE2_SmbProcessResponseData(ssd, nb_ptr, dcnt) != DCE2_RET__SUCCESS)
+ return DCE2_RET__ERROR;
+
+ break;
+ }
+ case TRANS_SET_NMPIPE_STATE:
+ DebugFormat(DEBUG_DCE_SMB, "Setting pipe "
+ "to %s mode\n", ttracker->pipe_byte_mode ? "byte" : "message");
+ ssd->cur_rtracker->ftracker->fp_byte_mode = ttracker->pipe_byte_mode;
+ break;
+
+ case TRANS_WRITE_NMPIPE:
+ break;
+
+ default:
+ return DCE2_RET__ERROR;
+ }
+
+ if (ttracker->disconnect_tid)
+ DCE2_SmbRemoveTid(ssd, ssd->cur_rtracker->tid);
+ }
+
+ return DCE2_RET__SUCCESS;
+}
+
+#define DCE2_SMB_TRANS__NONE 0x00
+#define DCE2_SMB_TRANS__DATA 0x01
+#define DCE2_SMB_TRANS__PARAMS 0x02
+#define DCE2_SMB_TRANS__BOTH (DCE2_SMB_TRANS__DATA|DCE2_SMB_TRANS__PARAMS)
+
+/********************************************************************
+ * Function: DCE2_SmbUpdateTransRequest()
+ *
+ * Purpose:
+ * Handles common checks and updates of transaction requests -
+ * SMB_COM_TRANSACTION, SMB_COM_TRANSACTION2 and SMB_COM_NT_TRANSACT
+ *
+ * Arguments:
+ * DCE2_SmbSsnData * - pointer to SMB session data
+ * const SmbNtHdr * - pointer to SMB header
+ * const DCE2_SmbComInfo * - pointer to com info structure
+ * const uint8_t * - pointer to data
+ * uint32_t - data length
+ *
+ * Returns:
+ * DCE2_Ret
+ * DCE2_RET__IGNORE if we don't process the subcommand
+ * DCE2_RET__FULL if the transaction is complete
+ * DCE2_RET__ERROR if an error occurred.
+ * DCE2_RET__SUCCESS if ok (but not complete).
+ *
+ ********************************************************************/
+static DCE2_Ret DCE2_SmbUpdateTransRequest(DCE2_SmbSsnData* ssd,
+ const SmbNtHdr* smb_hdr, const DCE2_SmbComInfo* com_info,
+ const uint8_t* nb_ptr, uint32_t nb_len)
+{
+ uint32_t tpcnt, pcnt, poff;
+ uint32_t tdcnt, dcnt, doff;
+ uint16_t com_size = DCE2_ComInfoCommandSize(com_info);
+ uint16_t byte_count = DCE2_ComInfoByteCount(com_info);
+ uint16_t fid;
+ uint8_t setup_count;
+ DCE2_SmbTransactionTracker* ttracker = &ssd->cur_rtracker->ttracker;
+ uint16_t sub_com =0;
+ int data_params = DCE2_SMB_TRANS__NONE;
+ uint8_t smb_com = DCE2_ComInfoSmbCom(com_info);
+
+ //FIXIT-M init to avoid warnings. can be removed once other commands are supported
+ tpcnt =0; pcnt =0; poff =0;
+ tdcnt =0; dcnt =0; doff =0;
+
+ switch (smb_com)
+ {
+ case SMB_COM_TRANSACTION:
+ sub_com = SmbTransactionReqSubCom((SmbTransactionReq*)nb_ptr);
+ fid = SmbTransactionReqFid((SmbTransactionReq*)nb_ptr);
+ setup_count = SmbTransactionReqSetupCnt((SmbTransactionReq*)nb_ptr);
+ tdcnt = SmbTransactionReqTotalDataCnt((SmbTransactionReq*)nb_ptr);
+ doff = SmbTransactionReqDataOff((SmbTransactionReq*)nb_ptr);
+ dcnt = SmbTransactionReqDataCnt((SmbTransactionReq*)nb_ptr);
+ tpcnt = SmbTransactionReqTotalParamCnt((SmbTransactionReq*)nb_ptr);
+ pcnt = SmbTransactionReqParamCnt((SmbTransactionReq*)nb_ptr);
+ poff = SmbTransactionReqParamOff((SmbTransactionReq*)nb_ptr);
+
+ DebugFormat(DEBUG_DCE_SMB,
+ "Transaction subcommand: %s (0x%04X)\n",
+ (sub_com < TRANS_SUBCOM_MAX)
+ ? smb_transaction_sub_command_strings[sub_com]
+ : "Unknown", sub_com);
+
+ ssd->cur_rtracker->ftracker = DCE2_SmbGetFileTracker(ssd, fid);
+ if (ssd->cur_rtracker->ftracker == nullptr)
+ return DCE2_RET__IGNORE;
+
+ switch (sub_com)
+ {
+ case TRANS_TRANSACT_NMPIPE:
+ if (DCE2_SsnIsWindowsPolicy(&ssd->sd)
+ && ssd->cur_rtracker->ftracker->fp_byte_mode)
+ {
+ DebugMessage(DEBUG_DCE_SMB, "Pipe is in byte "
+ "mode - TRANS_TRANSACT_NMPIPE won't work\n");
+ return DCE2_RET__ERROR;
}
+ data_params = DCE2_SMB_TRANS__DATA;
+ break;
+
+ case TRANS_READ_NMPIPE:
+ dce_alert(GID_DCE2, DCE2_SMB_UNUSUAL_COMMAND_USED, (dce2CommonStats*)&dce2_smb_stats);
+ break;
+
+ case TRANS_SET_NMPIPE_STATE:
+ data_params = DCE2_SMB_TRANS__PARAMS;
+ break;
+
+ case TRANS_WRITE_NMPIPE:
+ dce_alert(GID_DCE2, DCE2_SMB_UNUSUAL_COMMAND_USED, (dce2CommonStats*)&dce2_smb_stats);
+ data_params = DCE2_SMB_TRANS__DATA;
+ break;
+
+ // Not implemented according to MS-CIFS
+ case TRANS_RAW_READ_NMPIPE:
+
+ // Can only write 2 NULL bytes and subsequent writes return pipe disconnected
+ case TRANS_RAW_WRITE_NMPIPE:
+
+ // Can at most do a DCE/RPC bind
+ case TRANS_CALL_NMPIPE:
+ dce_alert(GID_DCE2, DCE2_SMB_DEPR_COMMAND_USED, (dce2CommonStats*)&dce2_smb_stats);
+ // Aren't looking at these or the three above
+ case TRANS_QUERY_NMPIPE_STATE:
+ case TRANS_QUERY_NMPIPE_INFO:
+ case TRANS_PEEK_NMPIPE:
+ case TRANS_WAIT_NMPIPE:
+ default:
+ // Don't want to track the response
+ return DCE2_RET__IGNORE;
+ }
+
+ // Servers return error if incorrect setup count
+ if (setup_count != 2)
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_INVALID_SETUP_COUNT, (dce2CommonStats*)&dce2_smb_stats);
+ return DCE2_RET__ERROR;
+ }
+
+ DCE2_MOVE(nb_ptr, nb_len, com_size);
+
+ // Samba validates the Name which should be \PIPE\ and errors
+ // if not. Windows doesn't care.
+ // And Samba uses the ByteCount to validate
+ if (DCE2_SsnIsSambaPolicy(&ssd->sd)
+ && (DCE2_SmbTransactionGetName(nb_ptr, nb_len,
+ byte_count, SmbUnicode(smb_hdr)) != DCE2_RET__SUCCESS))
+ {
+ DebugMessage(DEBUG_DCE_SMB, "Failed to validate "
+ "pipe name for Samba.\n");
+ return DCE2_RET__ERROR;
+ }
+ break;
+
+ case SMB_COM_TRANSACTION2:
+ // FIXIT-M port together with transaction2
+
+ break;
+
+ case SMB_COM_NT_TRANSACT:
+ // FIXIT-M port together with nt_transact
+ break;
+
+ default:
+ return DCE2_RET__ERROR;
+ }
+
+ if (DCE2_SmbValidateTransactionFields((uint8_t*)smb_hdr, nb_ptr, nb_len,
+ byte_count, tdcnt, tpcnt, dcnt, doff, 0, pcnt, poff, 0) != DCE2_RET__SUCCESS)
+ return DCE2_RET__ERROR;
+
+ ttracker->smb_type = SMB_TYPE__REQUEST;
+ ttracker->subcom = (uint8_t)sub_com;
+ ttracker->tdcnt = tdcnt;
+ ttracker->dsent = dcnt;
+ ttracker->tpcnt = tpcnt;
+ ttracker->psent = pcnt;
+
+ DebugFormat(DEBUG_DCE_SMB, "Data count: %u, "
+ "Total data count: %u, Param count: %u, "
+ "Total param count: %u\n", dcnt, tdcnt, pcnt, tpcnt);
+
+ // Testing shows that Transacts aren't processed until
+ // all of the data and parameters are received, so overlapping
+ // writes to the same FID can occur as long as the pid/mid are
+ // distinct (and that depends on policy). So we need to buffer
+ // data up for each incomplete Transact so data doesn't get mangled
+ // together with multiple ones intermixing at the same time.
+
+ if (data_params & DCE2_SMB_TRANS__DATA)
+ {
+ // FIXIT-M port
+ }
+
+ if (data_params & DCE2_SMB_TRANS__PARAMS)
+ {
+ // FIXIT-M port
+ }
+
+ if ((dcnt == tdcnt) && (pcnt == tpcnt))
+ return DCE2_RET__FULL;
+
+ return DCE2_RET__SUCCESS;
+}
+
+/********************************************************************
+ * Function: DCE2_SmbUpdateTransResponse()
+ *
+ * Purpose:
+ * Handles common checks and updates of transaction responses -
+ * SMB_COM_TRANSACTION, SMB_COM_TRANSACTION2 and SMB_COM_NT_TRANSACT
+ *
+ * Arguments:
+ * DCE2_SmbSsnData * - pointer to SMB session data
+ * const SmbNtHdr * - pointer to SMB header
+ * const DCE2_SmbComInfo * - pointer to com info structure
+ * const uint8_t * - pointer to data
+ * uint32_t - data length
+ *
+ * Returns:
+ * DCE2_Ret
+ * DCE2_RET__FULL if the transaction is complete
+ * DCE2_RET__ERROR if an error occurred.
+ * DCE2_RET__SUCCESS if ok (but not complete).
+ *
+ ********************************************************************/
+static DCE2_Ret DCE2_SmbUpdateTransResponse(DCE2_SmbSsnData* ssd,
+ const SmbNtHdr* smb_hdr, const DCE2_SmbComInfo* com_info,
+ const uint8_t* nb_ptr, uint32_t nb_len)
+{
+ uint32_t tpcnt, pcnt, poff, pdisp;
+ uint32_t tdcnt, dcnt, doff, ddisp;
+ DCE2_SmbTransactionTracker* ttracker = &ssd->cur_rtracker->ttracker;
+ uint16_t sub_com = ttracker->subcom;
+ int data_params = DCE2_SMB_TRANS__NONE;
+ uint8_t smb_com = DCE2_ComInfoSmbCom(com_info);
+
+ //FIXIT-M init to avoid warnings. can be removed once other commands are supported
+ tpcnt =0; pcnt =0; poff =0; pdisp =0;
+ tdcnt =0; dcnt =0; doff =0; ddisp =0;
+ switch (smb_com)
+ {
+ case SMB_COM_TRANSACTION:
+ tdcnt = SmbTransactionRespTotalDataCnt((SmbTransactionResp*)nb_ptr);
+ doff = SmbTransactionRespDataOff((SmbTransactionResp*)nb_ptr);
+ dcnt = SmbTransactionRespDataCnt((SmbTransactionResp*)nb_ptr);
+ ddisp = SmbTransactionRespDataDisp((SmbTransactionResp*)nb_ptr);
+ tpcnt = SmbTransactionRespTotalParamCnt((SmbTransactionResp*)nb_ptr);
+ pcnt = SmbTransactionRespParamCnt((SmbTransactionResp*)nb_ptr);
+ poff = SmbTransactionRespParamOff((SmbTransactionResp*)nb_ptr);
+ pdisp = SmbTransactionRespParamDisp((SmbTransactionResp*)nb_ptr);
+
+ DebugFormat(DEBUG_DCE_SMB,
+ "Transaction subcommand: %s (0x%04X)\n",
+ (sub_com < TRANS_SUBCOM_MAX)
+ ? smb_transaction_sub_command_strings[sub_com]
+ : "Unknown", sub_com);
+
+ switch (sub_com)
+ {
+ case TRANS_TRANSACT_NMPIPE:
+ case TRANS_READ_NMPIPE:
+ data_params = DCE2_SMB_TRANS__DATA;
+ break;
+ case TRANS_SET_NMPIPE_STATE:
+ case TRANS_WRITE_NMPIPE:
+ data_params = DCE2_SMB_TRANS__PARAMS;
+ break;
+ default:
+ return DCE2_RET__ERROR;
+ }
+
+ break;
+
+ case SMB_COM_TRANSACTION2:
+ // FIXIT-M port along with transaction2
+
+ break;
+
+ case SMB_COM_NT_TRANSACT:
+ // FIXIT-M port along with NT_TRANSACT
+
+ break;
+
+ default:
+ return DCE2_RET__ERROR;
+ }
+
+ DCE2_MOVE(nb_ptr, nb_len, DCE2_ComInfoCommandSize(com_info));
+
+ // From client request
+ if (ttracker->smb_type == SMB_TYPE__REQUEST)
+ {
+ ttracker->smb_type = SMB_TYPE__RESPONSE;
+ ttracker->tdcnt = tdcnt;
+ ttracker->tpcnt = tpcnt;
+ ttracker->dsent = 0;
+ ttracker->psent = 0;
+ DCE2_BufferDestroy(ttracker->dbuf);
+ ttracker->dbuf = nullptr;
+ DCE2_BufferDestroy(ttracker->pbuf);
+ ttracker->pbuf = nullptr;
+ }
+ else
+ {
+ if (tdcnt < ttracker->tdcnt)
+ ttracker->tdcnt = tdcnt;
+ if (tpcnt < ttracker->tpcnt)
+ ttracker->tpcnt = pcnt;
+ }
+
+ if (DCE2_SmbValidateTransactionFields((uint8_t*)smb_hdr, nb_ptr, nb_len,
+ DCE2_ComInfoByteCount(com_info), tdcnt, tpcnt, dcnt, doff, ddisp,
+ pcnt, poff, pdisp) != DCE2_RET__SUCCESS)
+ return DCE2_RET__ERROR;
+
+ if (DCE2_SmbValidateTransactionSent(ttracker->dsent, dcnt, ttracker->tdcnt,
+ ttracker->psent, pcnt, ttracker->tpcnt) != DCE2_RET__SUCCESS)
+ return DCE2_RET__ERROR;
+
+ ttracker->dsent += dcnt;
+ ttracker->psent += pcnt;
+
+ DebugFormat(DEBUG_DCE_SMB, "Data displacement: %u, "
+ "Data count: %u, Total data count: %u\n"
+ "Parameter displacement: %u, "
+ "Parameter count: %u, Total parameter count: %u\n",
+ ddisp, dcnt, tdcnt, pdisp, pcnt, tpcnt);
+
+ if (data_params & DCE2_SMB_TRANS__DATA)
+ {
+ // FIXIT-M port
+ }
+
+ if (data_params & DCE2_SMB_TRANS__PARAMS)
+ {
+ // FIXIT-M port
+ }
+
+ if ((ttracker->dsent == ttracker->tdcnt)
+ && (ttracker->psent == ttracker->tpcnt))
+ {
+ return DCE2_RET__FULL;
+ }
+
+ return DCE2_RET__SUCCESS;
+}
+
+/********************************************************************
+ * Function: DCE2_SmbCheckTotalCount()
+ *
+ * Purpose:
+ * Validates the advertised total data/param count. Makes sure the
+ * current count isn't greater than total count, that the
+ * displacement + count isn't greater than the total data count and
+ * that the total data count isn't zero. Mainly relevant to Write Raw,
+ * Transaction and Transaction Secondary commands.
+ *
+ * Arguments:
+ * const uint32_t - total data count
+ * const uint32_t - data count/size
+ * const uint32_t - data displacement
+ *
+ * Returns:
+ * DCE2_Ret - DCE2_RET__SUCCESS if all is ok
+ * DCE2_RET__ERROR if any of the checks fail.
+ *
+ ********************************************************************/
+static DCE2_Ret DCE2_SmbCheckTotalCount(const uint32_t tcnt, const uint32_t cnt, const uint32_t
+ disp)
+{
+ DCE2_Ret ret = DCE2_RET__SUCCESS;
+
+ if (cnt > tcnt)
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_TDCNT_LT_DSIZE, (dce2CommonStats*)&dce2_smb_stats);
+ ret = DCE2_RET__ERROR;
+ }
+
+ if (((uint64_t)disp + cnt) > tcnt)
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_DSENT_GT_TDCNT, (dce2CommonStats*)&dce2_smb_stats);
+ ret = DCE2_RET__ERROR;
+ }
+
+ return ret;
+}
+
+/********************************************************************
+ * Function: DCE2_SmbCheckTransDataParams()
+ *
+ * Purpose:
+ * Ensures that the data size reported in an SMB command is kosher.
+ * Note the 32 bit values are because of the NtTransact command
+ * though it's currently not checked.
+ *
+ * Arguments:
+ * const uint8_t * - pointer to start of SMB header where offset is
+ * taken from.
+ * const uint8_t * - current pointer - should be right after command
+ * structure.
+ * const uint32_t - remaining data left in PDU from current pointer.
+ * const uint16_t - the byte count
+ * const uint32_t - reported data count
+ * const uint32_t - reported data offset
+ * const uint32_t - reported parameter count
+ * const uint32_t - reported parameter offset
+ *
+ * Returns:
+ * DCE2_Ret - DCE2_RET__ERROR if data should not be processed
+ * DCE2_RET__SUCCESS if data can be processed
+ *
+ ********************************************************************/
+static DCE2_Ret DCE2_SmbCheckTransDataParams(
+ const uint8_t* smb_hdr_ptr, const uint8_t* nb_ptr, const uint32_t nb_len,
+ const uint16_t bcc, const uint32_t dcnt, const uint32_t doff,
+ const uint32_t pcnt, const uint32_t poff)
+{
+ const uint8_t* doffset = smb_hdr_ptr + doff;
+ const uint8_t* poffset = smb_hdr_ptr + poff;
+ const uint8_t* nb_end = nb_ptr + nb_len;
+
+ if (bcc < ((uint64_t)dcnt + pcnt))
+ dce_alert(GID_DCE2, DCE2_SMB_BCC_LT_DSIZE, (dce2CommonStats*)&dce2_smb_stats);
+
+ // Check data offset out of bounds
+ if ((doffset > nb_end) || (doffset < smb_hdr_ptr))
+ {
+ // Beyond data left or wrap
+ dce_alert(GID_DCE2, DCE2_SMB_BAD_OFF, (dce2CommonStats*)&dce2_smb_stats);
+ return DCE2_RET__ERROR;
+ }
+
+ // Check data offset in bounds but backwards
+ // Only check if the data count is non-zero
+ if ((dcnt != 0) && (doffset < nb_ptr))
+ {
+ // Not necessarily and error if the offset puts the data
+ // before or in the command structure.
+ dce_alert(GID_DCE2, DCE2_SMB_BAD_OFF, (dce2CommonStats*)&dce2_smb_stats);
+ }
+
+ // Check the data offset + data count
+ if (((doffset + dcnt) > nb_end) // beyond data left
+ || ((doffset + dcnt) < doffset)) // wrap
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_NB_LT_DSIZE, (dce2CommonStats*)&dce2_smb_stats);
+ return DCE2_RET__ERROR;
+ }
+
+ // Check parameter offset out of bounds
+ if ((poffset > nb_end) || (poffset < smb_hdr_ptr))
+ {
+ // Beyond data left or wrap
+ dce_alert(GID_DCE2, DCE2_SMB_BAD_OFF, (dce2CommonStats*)&dce2_smb_stats);
+ return DCE2_RET__ERROR;
+ }
+
+ // Check parameter offset in bounds but backwards
+ // Only check if the parameter count is non-zero
+ if ((pcnt != 0) && (poffset < nb_ptr))
+ {
+ // Not necessarily and error if the offset puts the data
+ // before or in the command structure.
+ dce_alert(GID_DCE2, DCE2_SMB_BAD_OFF, (dce2CommonStats*)&dce2_smb_stats);
+ }
+
+ // Check the parameter offset + parameter count
+ if (((poffset + pcnt) > nb_end) // beyond data left
+ || ((poffset + pcnt) < poffset)) // wrap
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_NB_LT_DSIZE, (dce2CommonStats*)&dce2_smb_stats);
+ return DCE2_RET__ERROR;
+ }
+
+ return DCE2_RET__SUCCESS;
+}
+
+/********************************************************************
+ * Function: DCE2_SmbValidateTransactionSent()
+ *
+ * Purpose:
+ * Checks that amount sent plus current amount is not greater than
+ * the total count expected.
+ *
+ * Arguments:
+ * const uint32_t - amount of data sent so far
+ * const uint32_t - reported total data count
+ * const uint32_t - reported data count
+ * const uint32_t - amount of parameters sent so far
+ * const uint32_t - reported total parameter count
+ * const uint32_t - reported parameter count
+ *
+ * Returns:
+ * DCE2_Ret - DCE2_RET__ERROR if data should not be processed
+ * DCE2_RET__SUCCESS if data can be processed
+ *
+ ********************************************************************/
+static DCE2_Ret DCE2_SmbValidateTransactionSent(
+ uint32_t dsent, uint32_t dcnt, uint32_t tdcnt,
+ uint32_t psent, uint32_t pcnt, uint32_t tpcnt)
+{
+ if (((dsent + dcnt) > tdcnt) || ((psent + pcnt) > tpcnt))
+ {
+ if ((dsent + dcnt) > tdcnt)
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_DSENT_GT_TDCNT, (dce2CommonStats*)&dce2_smb_stats);
+ }
+
+ if ((psent + pcnt) > tpcnt)
+ {
+ dce_alert(GID_DCE2, DCE2_SMB_DSENT_GT_TDCNT, (dce2CommonStats*)&dce2_smb_stats);
}
+
+ // Samba throws out entire transaction and Windows seems to hang in
+ // limbo forever and never responds, so stop looking
+ return DCE2_RET__ERROR;
}
+
+ return DCE2_RET__SUCCESS;
+}
+
+/********************************************************************
+ * Function: DCE2_SmbValidateTransactionFields()
+ *
+ * Purpose:
+ * Wrapper that calls DCE2_SmbCheckTotalCount() for total parameter
+ * count and total data count and DCE2_SmbCheckTransDataParams()
+ *
+ * Arguments:
+ * const uint8_t * - pointer to start of SMB header where offset is
+ * taken from.
+ * const uint8_t * - current pointer - should be right after command
+ * structure.
+ * const uint32_t - remaining data left in PDU from current pointer.
+ * const uint16_t - the byte count
+ * const uint32_t - reported total data count
+ * const uint32_t - reported total parameter count
+ * const uint32_t - reported data count
+ * const uint32_t - reported data offset
+ * const uint32_t - reported data displacement
+ * const uint32_t - reported parameter count
+ * const uint32_t - reported parameter offset
+ * const uint32_t - reported parameter displacement
+ *
+ * Returns:
+ * DCE2_Ret - DCE2_RET__ERROR if data should not be processed
+ * DCE2_RET__SUCCESS if data can be processed
+ *
+ ********************************************************************/
+static inline DCE2_Ret DCE2_SmbValidateTransactionFields(
+ const uint8_t* smb_hdr_ptr,
+ const uint8_t* nb_ptr, const uint32_t nb_len, const uint16_t bcc,
+ const uint32_t tdcnt, const uint32_t tpcnt,
+ const uint32_t dcnt, const uint32_t doff, const uint32_t ddisp,
+ const uint32_t pcnt, const uint32_t poff, const uint32_t pdisp)
+{
+ if (DCE2_SmbCheckTotalCount(tdcnt, dcnt, ddisp) != DCE2_RET__SUCCESS)
+ return DCE2_RET__ERROR;
+
+ if (DCE2_SmbCheckTotalCount(tpcnt, pcnt, pdisp) != DCE2_RET__SUCCESS)
+ return DCE2_RET__ERROR;
+
+ if (DCE2_SmbCheckTransDataParams(smb_hdr_ptr,
+ nb_ptr, nb_len, bcc, dcnt, doff, pcnt, poff) != DCE2_RET__SUCCESS)
+ return DCE2_RET__ERROR;
+
+ return DCE2_RET__SUCCESS;
}
//-------------------------------------------------------------------------