1 //--------------------------------------------------------------------------
2 // Copyright (C) 2016-2021 Cisco and/or its affiliates. All rights reserved.
3 //
4 // This program is free software; you can redistribute it and/or modify it
5 // under the terms of the GNU General Public License Version 2 as published
6 // by the Free Software Foundation.  You may not use, modify or distribute
7 // this program under any other version of the GNU General Public License.
8 //
9 // This program is distributed in the hope that it will be useful, but
10 // WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License along
15 // with this program; if not, write to the Free Software Foundation, Inc.,
16 // 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 //--------------------------------------------------------------------------
18 
19 // dce_smb_utils.cc author Maya Dagon <mdagon@cisco.com>
20 // based on work by Todd Wease
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include "dce_smb_utils.h"
27 
28 #include "detection/detection_engine.h"
29 #include "detection/detection_util.h"
30 #include "file_api/file_api.h"
31 #include "hash/hash_key_operations.h"
32 #include "main/snort.h"
33 #include "main/snort_debug.h"
34 #include "network_inspectors/packet_tracer/packet_tracer.h"
35 #include "packet_io/active.h"
36 #include "utils/util.h"
37 
38 #include "dce_smb_module.h"
39 
40 using namespace snort;
41 
42 static uint8_t dce2_smb_delete_pdu[65535];
43 
44 /********************************************************************
45  * Private function prototypes
46  ********************************************************************/
47 static void DCE2_SmbSetNewFileAPIFileTracker(DCE2_SmbSsnData* ssd);
48 static void DCE2_SmbResetFileChunks(DCE2_SmbFileTracker* ssd);
49 static void DCE2_SmbFinishFileAPI(DCE2_SmbSsnData*);
50 static void DCE2_SmbFinishFileBlockVerdict(DCE2_SmbSsnData* ssd);
51 
52 /********************************************************************
53  * Inline functions
54  ********************************************************************/
55 
DCE2_SmbFileUpload(DCE2_SmbFileDirection dir)56 static inline bool DCE2_SmbFileUpload(DCE2_SmbFileDirection dir)
57 {
58     return dir == DCE2_SMB_FILE_DIRECTION__UPLOAD;
59 }
60 
DCE2_SmbFileDirUnknown(DCE2_SmbFileDirection dir)61 static inline bool DCE2_SmbFileDirUnknown(DCE2_SmbFileDirection dir)
62 {
63     return dir == DCE2_SMB_FILE_DIRECTION__UNKNOWN;
64 }
65 
66 /********************************************************************
67  * Function:  DCE2_SmbIsTidIPC()
68  *
69  * Purpose: Checks to see if the TID passed in was to IPC or not.
70  *
71  * Arguments:
72  *  DCE2_SmbSsnData * - pointer to session data
73  *  const uint16_t    - the TID to check
74  *
75  * Returns:
76  *  bool - True if TID is IPC, false if not or if TID not found.
77  *
78  ********************************************************************/
DCE2_SmbIsTidIPC(DCE2_SmbSsnData * ssd,const uint16_t tid)79 bool DCE2_SmbIsTidIPC(DCE2_SmbSsnData* ssd, const uint16_t tid)
80 {
81     if ((ssd->tid != DCE2_SENTINEL)
82         && ((ssd->tid & 0x0000ffff) == (int)tid))
83     {
84         if ((ssd->tid >> 16) == 0)
85             return true;
86     }
87     else
88     {
89         int check_tid = (int)(uintptr_t)DCE2_ListFind(ssd->tids, (void*)(uintptr_t)tid);
90         if (((check_tid & 0x0000ffff) == (int)tid) && ((check_tid >> 16) == 0))
91             return true;
92     }
93 
94     return false;
95 }
96 
DCE2_SmbUidTidFidCompare(const void * a,const void * b)97 int DCE2_SmbUidTidFidCompare(const void* a, const void* b)
98 {
99     int x = (int)(uintptr_t)a;
100     int y = (int)(uintptr_t)b;
101 
102     if (x == y)
103         return 0;
104 
105     /* Only care about equality for finding */
106     return -1;
107 }
108 
DCE2_SmbFindUid(DCE2_SmbSsnData * ssd,const uint16_t uid)109 DCE2_Ret DCE2_SmbFindUid(DCE2_SmbSsnData* ssd, const uint16_t uid)
110 {
111     DCE2_Ret status;
112 
113     if ((ssd->uid != DCE2_SENTINEL) && (ssd->uid == (int)uid))
114         status = DCE2_RET__SUCCESS;
115     else
116         status = DCE2_ListFindKey(ssd->uids, (void*)(uintptr_t)uid);
117 
118     return status;
119 }
120 
DCE2_SmbInsertUid(DCE2_SmbSsnData * ssd,const uint16_t uid)121 void DCE2_SmbInsertUid(DCE2_SmbSsnData* ssd, const uint16_t uid)
122 {
123     if (ssd->uid == DCE2_SENTINEL)
124     {
125         ssd->uid = (int)uid;
126     }
127     else
128     {
129         if (ssd->uids == nullptr)
130         {
131             ssd->uids = DCE2_ListNew(DCE2_LIST_TYPE__SPLAYED, DCE2_SmbUidTidFidCompare,
132                 nullptr, nullptr, DCE2_LIST_FLAG__NO_DUPS);
133 
134             if (ssd->uids == nullptr)
135             {
136                 return;
137             }
138         }
139 
140         DCE2_ListInsert(ssd->uids, (void*)(uintptr_t)uid, (void*)(uintptr_t)uid);
141     }
142 }
143 
DCE2_SmbRemoveUid(DCE2_SmbSsnData * ssd,const uint16_t uid)144 void DCE2_SmbRemoveUid(DCE2_SmbSsnData* ssd, const uint16_t uid)
145 {
146     const DCE2_Policy policy = DCE2_SsnGetServerPolicy(&ssd->sd);
147 
148     if ((ssd->uid != DCE2_SENTINEL) && (ssd->uid == (int)uid))
149         ssd->uid = DCE2_SENTINEL;
150     else
151         DCE2_ListRemove(ssd->uids, (void*)(uintptr_t)uid);
152 
153     switch (policy)
154     {
155     case DCE2_POLICY__WIN2000:
156     case DCE2_POLICY__WIN2003:
157     case DCE2_POLICY__WINXP:
158     case DCE2_POLICY__WINVISTA:
159     case DCE2_POLICY__WIN2008:
160     case DCE2_POLICY__WIN7:
161     case DCE2_POLICY__SAMBA:
162     case DCE2_POLICY__SAMBA_3_0_37:
163         // Removing uid invalidates any fid that was created with it */
164         if ((ssd->ftracker.fid_v1 != DCE2_SENTINEL) &&
165             (ssd->ftracker.uid_v1 == uid))
166         {
167             DCE2_SmbRemoveFileTracker(ssd, &ssd->ftracker);
168         }
169 
170         if (ssd->ftrackers != nullptr)
171         {
172             DCE2_SmbFileTracker* ftracker;
173 
174             for (ftracker = (DCE2_SmbFileTracker*)DCE2_ListFirst(ssd->ftrackers);
175                 ftracker != nullptr;
176                 ftracker = (DCE2_SmbFileTracker*)DCE2_ListNext(ssd->ftrackers))
177             {
178                 if (ftracker->uid_v1 == uid)
179                 {
180                     if (ssd->fapi_ftracker == ftracker)
181                         DCE2_SmbFinishFileAPI(ssd);
182 
183                     if (ssd->fb_ftracker == ftracker)
184                         DCE2_SmbFinishFileBlockVerdict(ssd);
185 
186                     DCE2_ListRemoveCurrent(ssd->ftrackers);
187                     DCE2_SmbRemoveFileTrackerFromRequestTrackers(ssd, ftracker);
188                 }
189             }
190         }
191 
192         break;
193 
194     case DCE2_POLICY__SAMBA_3_0_20:
195     case DCE2_POLICY__SAMBA_3_0_22:
196         // Removing Uid used to create file doesn't invalidate it.
197         break;
198 
199     default:
200         assert(false);
201         break;
202     }
203 }
204 
DCE2_SmbNewRequestTracker(DCE2_SmbSsnData * ssd,const SmbNtHdr * smb_hdr)205 DCE2_SmbRequestTracker* DCE2_SmbNewRequestTracker(DCE2_SmbSsnData* ssd,
206     const SmbNtHdr* smb_hdr)
207 {
208     uint16_t pid = SmbPid(smb_hdr);
209     uint16_t mid = SmbMid(smb_hdr);
210     uint16_t uid = SmbUid(smb_hdr);
211     uint16_t tid = SmbTid(smb_hdr);
212 
213     if (ssd == nullptr)
214     {
215         return nullptr;
216     }
217 
218     if (ssd->outstanding_requests >= ssd->max_outstanding_requests)
219     {
220         dce_alert(GID_DCE2, DCE2_SMB_MAX_REQS_EXCEEDED, (dce2CommonStats*)&dce2_smb_stats,
221             ssd->sd);
222     }
223 
224     // Check for outstanding requests with the same MID
225     DCE2_SmbRequestTracker* tmp_rtracker = &ssd->rtracker;
226     while ((tmp_rtracker != nullptr) && (tmp_rtracker->mid != DCE2_SENTINEL))
227     {
228         if (tmp_rtracker->mid == (int)mid)
229         {
230             // Have yet to see an MID repeatedly used so shouldn't
231             // be any outstanding requests with the same MID.
232             dce_alert(GID_DCE2, DCE2_SMB_REQS_SAME_MID, (dce2CommonStats*)&dce2_smb_stats,
233                 ssd->sd);
234             break;
235         }
236 
237         // Look at the next request in the queue
238         if (tmp_rtracker == &ssd->rtracker)
239             tmp_rtracker = (DCE2_SmbRequestTracker*)DCE2_QueueFirst(ssd->rtrackers);
240         else
241             tmp_rtracker = (DCE2_SmbRequestTracker*)DCE2_QueueNext(ssd->rtrackers);
242     }
243 
244     DCE2_SmbRequestTracker* rtracker = nullptr;
245     if (ssd->rtracker.mid == DCE2_SENTINEL)
246     {
247         rtracker = &ssd->rtracker;
248     }
249     else
250     {
251         if (ssd->rtrackers == nullptr)
252         {
253             ssd->rtrackers = DCE2_QueueNew(DCE2_SmbRequestTrackerDataFree);
254             if (ssd->rtrackers == nullptr)
255             {
256                 return nullptr;
257             }
258         }
259 
260         rtracker = (DCE2_SmbRequestTracker*)snort_calloc(sizeof(DCE2_SmbRequestTracker));
261         if (rtracker == nullptr)
262         {
263             return nullptr;
264         }
265 
266         if (DCE2_QueueEnqueue(ssd->rtrackers, (void*)rtracker) != DCE2_RET__SUCCESS)
267         {
268             snort_free((void*)rtracker);
269             return nullptr;
270         }
271     }
272 
273     rtracker->smb_com = SmbCom(smb_hdr);
274     rtracker->uid = uid;
275     rtracker->tid = tid;
276     rtracker->pid = pid;
277     rtracker->mid = (int)mid;
278     memset(&rtracker->ttracker, 0, sizeof(rtracker->ttracker));
279     rtracker->ftracker = nullptr;
280     rtracker->sequential_only = false;
281 
282     ssd->outstanding_requests++;
283     if (ssd->outstanding_requests > dce2_smb_stats.smb_max_outstanding_requests)
284         dce2_smb_stats.smb_max_outstanding_requests = ssd->outstanding_requests;
285 
286     return rtracker;
287 }
288 
DCE2_SmbNewFileTracker(DCE2_SmbSsnData * ssd,const uint16_t uid,const uint16_t tid,const uint16_t fid)289 DCE2_SmbFileTracker* DCE2_SmbNewFileTracker(DCE2_SmbSsnData* ssd,
290     const uint16_t uid, const uint16_t tid, const uint16_t fid)
291 {
292     // Already have tracker for file API and not setting file data pointer
293     // so don't create new file tracker.
294     bool is_ipc = DCE2_SmbIsTidIPC(ssd, tid);
295     if (!is_ipc && (ssd->fapi_ftracker != nullptr)
296         && (DCE2_ScSmbFileDepth((dce2SmbProtoConf*)ssd->sd.config) == -1))
297         return nullptr;
298 
299     DCE2_SmbFileTracker* ftracker = nullptr;
300     if (ssd->ftracker.fid_v1 == DCE2_SENTINEL)
301     {
302         ftracker = &ssd->ftracker;
303         if (DCE2_SmbInitFileTracker(ssd, ftracker, is_ipc, uid, tid, (int)fid) !=
304             DCE2_RET__SUCCESS)
305         {
306             DCE2_SmbCleanFileTracker(ftracker);
307             return nullptr;
308         }
309     }
310     else
311     {
312         ftracker = (DCE2_SmbFileTracker*)snort_calloc(sizeof(DCE2_SmbFileTracker));
313 
314         if (ftracker == nullptr)
315         {
316             return nullptr;
317         }
318 
319         if (DCE2_SmbInitFileTracker(ssd, ftracker, is_ipc, uid, tid, (int)fid) !=
320             DCE2_RET__SUCCESS)
321         {
322             DCE2_SmbCleanFileTracker(ftracker);
323             snort_free((void*)ftracker);
324             return nullptr;
325         }
326 
327         if (ssd->ftrackers == nullptr)
328         {
329             ssd->ftrackers = DCE2_ListNew(DCE2_LIST_TYPE__SPLAYED,
330                 DCE2_SmbUidTidFidCompare, DCE2_SmbFileTrackerDataFree, nullptr,
331                 DCE2_LIST_FLAG__NO_DUPS);
332 
333             if (ssd->ftrackers == nullptr)
334             {
335                 DCE2_SmbCleanSessionFileTracker(ssd, ftracker);
336                 return nullptr;
337             }
338         }
339 
340         if (DCE2_ListInsert(ssd->ftrackers, (void*)(uintptr_t)fid,
341             (void*)ftracker) != DCE2_RET__SUCCESS)
342         {
343             DCE2_SmbCleanSessionFileTracker(ssd, ftracker);
344             return nullptr;
345         }
346     }
347 
348     return ftracker;
349 }
350 
DCE2_SmbInitFileTracker(DCE2_SmbSsnData * ssd,DCE2_SmbFileTracker * ftracker,const bool is_ipc,const uint16_t uid,const uint16_t tid,const int fid)351 DCE2_Ret DCE2_SmbInitFileTracker(DCE2_SmbSsnData* ssd,
352     DCE2_SmbFileTracker* ftracker, const bool is_ipc, const uint16_t uid,
353     const uint16_t tid, const int fid)
354 {
355     if (ftracker == nullptr)
356         return DCE2_RET__ERROR;
357 
358     ftracker->uid_v1 = uid;
359     ftracker->tid_v1 = tid;
360     ftracker->fid_v1 = fid;
361     ftracker->is_ipc = is_ipc;
362     ftracker->is_smb2 = false;
363     ftracker->file_name = nullptr;
364     ftracker->file_name_size = 0;
365     ftracker->file_name_hash = 0;
366     if (is_ipc)
367     {
368         DCE2_CoTracker* co_tracker = (DCE2_CoTracker*)snort_calloc(sizeof(DCE2_CoTracker));
369         if (co_tracker == nullptr)
370             return DCE2_RET__ERROR;
371         DCE2_CoInitTracker(co_tracker);
372         ftracker->fp_co_tracker = co_tracker;
373         ftracker->fp_byte_mode = false;
374         ftracker->fp_used = false;
375         ftracker->fp_writex_raw = nullptr;
376     }
377     else
378     {
379         ftracker->ff_file_size = 0;
380         ftracker->ff_file_offset = 0;
381         ftracker->ff_bytes_processed = 0;
382         ftracker->ff_file_direction = DCE2_SMB_FILE_DIRECTION__UNKNOWN;
383         ftracker->ff_file_chunks = nullptr;
384         ftracker->ff_bytes_queued = 0;
385         if ((ssd->fapi_ftracker == nullptr) && (ssd->max_file_depth != -1))
386         {
387             ssd->fapi_ftracker = ftracker;
388         }
389     }
390 
391     return DCE2_RET__SUCCESS;
392 }
393 
DCE2_SmbFindFileTracker(DCE2_SmbSsnData * ssd,const uint16_t uid,const uint16_t tid,const uint16_t fid)394 DCE2_SmbFileTracker* DCE2_SmbFindFileTracker(DCE2_SmbSsnData* ssd,
395     const uint16_t uid, const uint16_t tid, const uint16_t fid)
396 {
397     DCE2_SmbFileTracker* ftracker;
398     if ((ssd->ftracker.fid_v1 != DCE2_SENTINEL) && (ssd->ftracker.fid_v1 == (int)fid))
399     {
400         ftracker = &ssd->ftracker;
401     }
402     else
403     {
404         ftracker = (DCE2_SmbFileTracker*)
405             DCE2_ListFind(ssd->ftrackers, (void*)(uintptr_t)fid);
406     }
407 
408     if (ftracker == nullptr)
409     {
410         return nullptr;
411     }
412 
413     // Note IPC Tid has already been validated in initial processing
414     const DCE2_Policy policy = DCE2_SsnGetServerPolicy(&ssd->sd);
415     switch (policy)
416     {
417     case DCE2_POLICY__SAMBA:
418     case DCE2_POLICY__SAMBA_3_0_37:
419         // Only Uid used to open file can be used to make a request
420         if (ftracker->uid_v1 != uid)
421         {
422             return nullptr;
423         }
424 
425         break;
426 
427     case DCE2_POLICY__WIN2000:
428     case DCE2_POLICY__SAMBA_3_0_20:
429     case DCE2_POLICY__SAMBA_3_0_22:
430         // Any valid Uid can be used to make a request to a file ...
431         // except for Windows 2000 on the first use.
432         if ((policy != DCE2_POLICY__WIN2000) || (ftracker->is_ipc && ftracker->fp_used))
433         {
434             // Check that the Uid exists
435             if (DCE2_SmbFindUid(ssd, uid) != DCE2_RET__SUCCESS)
436             {
437                 return nullptr;
438             }
439 
440             break;
441         }
442         // fallthrough
443 
444     case DCE2_POLICY__WIN2003:
445     case DCE2_POLICY__WINXP:
446     case DCE2_POLICY__WINVISTA:
447     case DCE2_POLICY__WIN2008:
448     case DCE2_POLICY__WIN7:
449         // Both Uid and Tid used to create file must be used to make a request
450         if ((ftracker->uid_v1 != uid) || (ftracker->tid_v1 != tid))
451         {
452             return nullptr;
453         }
454 
455         break;
456 
457     default:
458         assert(false);
459         break;
460     }
461 
462     return ftracker;
463 }
464 
DCE2_SmbGetFileTracker(DCE2_SmbSsnData * ssd,const uint16_t fid)465 DCE2_SmbFileTracker* DCE2_SmbGetFileTracker(DCE2_SmbSsnData* ssd,
466     const uint16_t fid)
467 {
468     DCE2_SmbFileTracker* ftracker = ssd->cur_rtracker->ftracker;
469 
470     if (ftracker == nullptr)
471     {
472         // Write could've been chained to an OpenAndX or NtCreateAndX so a
473         // temporary file tracker would've been created until we get the
474         // response with the Fid returned from the OpenAndX / NtCreateAndX
475         ftracker = DCE2_SmbGetTmpFileTracker(ssd->cur_rtracker);
476         if (ftracker == nullptr)
477         {
478             // Otherwise find it with the passed in Fid
479             ftracker = DCE2_SmbFindFileTracker(ssd, ssd->cur_rtracker->uid,
480                 ssd->cur_rtracker->tid, fid);
481         }
482     }
483 
484     return ftracker;
485 }
486 
DCE2_SmbGetTmpFileTracker(DCE2_SmbRequestTracker * rtracker)487 DCE2_SmbFileTracker* DCE2_SmbGetTmpFileTracker(DCE2_SmbRequestTracker* rtracker)
488 {
489     if (!DCE2_QueueIsEmpty(rtracker->ft_queue))
490         return (DCE2_SmbFileTracker*)DCE2_QueueLast(rtracker->ft_queue);
491     return nullptr;
492 }
493 
DCE2_SmbRemoveFileTracker(DCE2_SmbSsnData * ssd,DCE2_SmbFileTracker * ftracker)494 void DCE2_SmbRemoveFileTracker(DCE2_SmbSsnData* ssd, DCE2_SmbFileTracker* ftracker)
495 {
496     if (ftracker == nullptr)
497         return;
498 
499     if (ssd->fapi_ftracker == ftracker)
500         DCE2_SmbFinishFileAPI(ssd);
501 
502     if (ssd->fb_ftracker == ftracker)
503         DCE2_SmbFinishFileBlockVerdict(ssd);
504 
505     if (ftracker == &ssd->ftracker)
506         DCE2_SmbCleanFileTracker(&ssd->ftracker);
507     else if (ssd->ftrackers != nullptr)
508         DCE2_ListRemove(ssd->ftrackers, (void*)(uintptr_t)ftracker->fid_v1);
509 
510     DCE2_SmbRemoveFileTrackerFromRequestTrackers(ssd, ftracker);
511 }
512 
DCE2_SmbCleanFileTracker(DCE2_SmbFileTracker * ftracker)513 void DCE2_SmbCleanFileTracker(DCE2_SmbFileTracker* ftracker)
514 {
515     if (ftracker == nullptr)
516         return;
517 
518     ftracker->fid_v1 = DCE2_SENTINEL;
519     if (ftracker->file_name != nullptr)
520     {
521         snort_free((void*)ftracker->file_name);
522         ftracker->file_name = nullptr;
523         ftracker->file_name_size = 0;
524         ftracker->file_name_hash = 0;
525     }
526 
527     if (ftracker->is_ipc)
528     {
529         ftracker->fp_used = false;
530         ftracker->fp_byte_mode = false;
531 
532         if (ftracker->fp_writex_raw != nullptr)
533         {
534             DCE2_BufferDestroy(ftracker->fp_writex_raw->buf);
535             snort_free((void*)ftracker->fp_writex_raw);
536             ftracker->fp_writex_raw = nullptr;
537         }
538 
539         if (ftracker->fp_co_tracker != nullptr)
540         {
541             DCE2_CoCleanTracker(ftracker->fp_co_tracker);
542             snort_free((void*)ftracker->fp_co_tracker);
543             ftracker->fp_co_tracker = nullptr;
544         }
545     }
546     else
547     {
548         ftracker->ff_file_size = 0;
549         ftracker->ff_file_offset = 0;
550         ftracker->ff_bytes_processed = 0;
551         ftracker->ff_file_direction = DCE2_SMB_FILE_DIRECTION__UNKNOWN;
552         ftracker->ff_bytes_queued = 0;
553         ftracker->ff_sequential_only = false;
554         if (ftracker->ff_file_chunks != nullptr)
555         {
556             DCE2_ListDestroy(ftracker->ff_file_chunks);
557             ftracker->ff_file_chunks = nullptr;
558         }
559     }
560 }
561 
DCE2_SmbFileTrackerDataFree(void * data)562 void DCE2_SmbFileTrackerDataFree(void* data)
563 {
564     DCE2_SmbFileTracker* ftracker = (DCE2_SmbFileTracker*)data;
565 
566     if (ftracker == nullptr)
567         return;
568 
569     DCE2_SmbCleanFileTracker(ftracker);
570     snort_free((void*)ftracker);
571 }
572 
573 /********************************************************************
574  *
575  * Remove file tracker and associated pointers in session
576  *
577  ********************************************************************/
DCE2_SmbCleanSessionFileTracker(DCE2_SmbSsnData * ssd,DCE2_SmbFileTracker * ftracker)578 void DCE2_SmbCleanSessionFileTracker(DCE2_SmbSsnData* ssd, DCE2_SmbFileTracker* ftracker)
579 {
580     DCE2_SmbCleanFileTracker(ftracker);
581     snort_free((void*)ftracker);
582     if (ssd->fapi_ftracker == ftracker)
583         ssd->fapi_ftracker = nullptr;
584 }
585 
DCE2_SmbCleanTransactionTracker(DCE2_SmbTransactionTracker * ttracker)586 void DCE2_SmbCleanTransactionTracker(DCE2_SmbTransactionTracker* ttracker)
587 {
588     if (ttracker == nullptr)
589     {
590         return;
591     }
592 
593     if (ttracker->dbuf != nullptr)
594         DCE2_BufferDestroy(ttracker->dbuf);
595 
596     if (ttracker->pbuf != nullptr)
597         DCE2_BufferDestroy(ttracker->pbuf);
598 
599     memset(ttracker, 0, sizeof(*ttracker));
600 }
601 
DCE2_SmbCleanRequestTracker(DCE2_SmbRequestTracker * rtracker)602 void DCE2_SmbCleanRequestTracker(DCE2_SmbRequestTracker* rtracker)
603 {
604     if (rtracker == nullptr)
605     {
606         return;
607     }
608 
609     if (rtracker->mid == DCE2_SENTINEL)
610     {
611         return;
612     }
613 
614     rtracker->mid = DCE2_SENTINEL;
615     rtracker->ftracker = nullptr;
616     rtracker->sequential_only = false;
617 
618     DCE2_SmbCleanTransactionTracker(&rtracker->ttracker);
619 
620     DCE2_QueueDestroy(rtracker->ft_queue);
621     rtracker->ft_queue = nullptr;
622 
623     if (rtracker->file_name != nullptr)
624     {
625         snort_free((void*)rtracker->file_name);
626         rtracker->file_name = nullptr;
627         rtracker->file_name_size = 0;
628     }
629 }
630 
DCE2_SmbRemoveRequestTracker(DCE2_SmbSsnData * ssd,DCE2_SmbRequestTracker * rtracker)631 void DCE2_SmbRemoveRequestTracker(DCE2_SmbSsnData* ssd,
632     DCE2_SmbRequestTracker* rtracker)
633 {
634     if ((ssd == nullptr) || (rtracker == nullptr))
635     {
636         return;
637     }
638 
639     if (rtracker == &ssd->rtracker)
640     {
641         DCE2_SmbCleanRequestTracker(&ssd->rtracker);
642         ssd->outstanding_requests--;
643         return;
644     }
645 
646     DCE2_SmbRequestTracker* tmp_node;
647     for (tmp_node = (DCE2_SmbRequestTracker*)DCE2_QueueFirst(ssd->rtrackers);
648         tmp_node != nullptr;
649         tmp_node = (DCE2_SmbRequestTracker*)DCE2_QueueNext(ssd->rtrackers))
650     {
651         if (tmp_node == (void*)rtracker)
652         {
653             DCE2_QueueRemoveCurrent(ssd->rtrackers);
654             ssd->outstanding_requests--;
655             return;
656         }
657     }
658 }
659 
DCE2_SmbRemoveFileTrackerFromRequestTrackers(DCE2_SmbSsnData * ssd,DCE2_SmbFileTracker * ftracker)660 void DCE2_SmbRemoveFileTrackerFromRequestTrackers(DCE2_SmbSsnData* ssd,
661     DCE2_SmbFileTracker* ftracker)
662 {
663     if (ftracker == nullptr)
664         return;
665 
666     // null out file trackers of any outstanding requests
667     // that reference this file tracker
668     if (ssd->rtracker.ftracker == ftracker)
669         ssd->rtracker.ftracker = nullptr;
670 
671     if ((ssd->cur_rtracker != nullptr) && (ssd->cur_rtracker->ftracker == ftracker))
672         ssd->cur_rtracker->ftracker = nullptr;
673 
674     DCE2_SmbRequestTracker* rtracker;
675     for (rtracker = (DCE2_SmbRequestTracker*)DCE2_QueueFirst(ssd->rtrackers);
676         rtracker != nullptr;
677         rtracker = (DCE2_SmbRequestTracker*)DCE2_QueueNext(ssd->rtrackers))
678     {
679         if (rtracker->ftracker == ftracker)
680             rtracker->ftracker = nullptr;
681     }
682 }
683 
DCE2_SmbDequeueTmpFileTracker(DCE2_SmbSsnData * ssd,DCE2_SmbRequestTracker * rtracker,const uint16_t fid)684 DCE2_SmbFileTracker* DCE2_SmbDequeueTmpFileTracker(DCE2_SmbSsnData* ssd,
685     DCE2_SmbRequestTracker* rtracker, const uint16_t fid)
686 {
687     DCE2_SmbFileTracker* ftracker = (DCE2_SmbFileTracker*)DCE2_QueueDequeue(rtracker->ft_queue);
688 
689     if (ftracker == nullptr)
690     {
691         return nullptr;
692     }
693 
694     if (ssd->ftracker.fid_v1 == DCE2_SENTINEL)
695     {
696         memcpy(&ssd->ftracker, ftracker, sizeof(DCE2_SmbFileTracker));
697         snort_free((void*)ftracker);
698         if (ssd->fapi_ftracker == ftracker)
699             ssd->fapi_ftracker = &ssd->ftracker;
700         ftracker = &ssd->ftracker;
701     }
702     else
703     {
704         if (ssd->ftrackers == nullptr)
705         {
706             ssd->ftrackers = DCE2_ListNew(DCE2_LIST_TYPE__SPLAYED,
707                 DCE2_SmbUidTidFidCompare, DCE2_SmbFileTrackerDataFree, nullptr,
708                 DCE2_LIST_FLAG__NO_DUPS);
709 
710             if (ssd->ftrackers == nullptr)
711             {
712                 DCE2_SmbCleanSessionFileTracker(ssd, ftracker);
713                 return nullptr;
714             }
715         }
716 
717         if (DCE2_ListInsert(ssd->ftrackers, (void*)(uintptr_t)fid,
718             (void*)ftracker) != DCE2_RET__SUCCESS)
719         {
720             DCE2_SmbCleanSessionFileTracker(ssd, ftracker);
721             return nullptr;
722         }
723     }
724 
725     // Other values were initialized when queuing.
726     ftracker->fid_v1 = (int)fid;
727 
728     return ftracker;
729 }
730 
DCE2_SmbRequestTrackerDataFree(void * data)731 void DCE2_SmbRequestTrackerDataFree(void* data)
732 {
733     DCE2_SmbRequestTracker* rtracker = (DCE2_SmbRequestTracker*)data;
734 
735     if (rtracker == nullptr)
736         return;
737 
738     DCE2_SmbCleanRequestTracker(rtracker);
739     snort_free((void*)rtracker);
740 }
741 
DCE2_SmbFindTid(DCE2_SmbSsnData * ssd,const uint16_t tid)742 DCE2_Ret DCE2_SmbFindTid(DCE2_SmbSsnData* ssd, const uint16_t tid)
743 {
744     DCE2_Ret status;
745 
746     if ((ssd->tid != DCE2_SENTINEL) && ((ssd->tid & 0x0000ffff) == (int)tid))
747         status = DCE2_RET__SUCCESS;
748     else
749         status = DCE2_ListFindKey(ssd->tids, (void*)(uintptr_t)tid);
750 
751     return status;
752 }
753 
DCE2_SmbRemoveTid(DCE2_SmbSsnData * ssd,const uint16_t tid)754 void DCE2_SmbRemoveTid(DCE2_SmbSsnData* ssd, const uint16_t tid)
755 {
756     if ((ssd->tid != DCE2_SENTINEL) && ((ssd->tid & 0x0000ffff) == (int)tid))
757         ssd->tid = DCE2_SENTINEL;
758     else
759         DCE2_ListRemove(ssd->tids, (void*)(uintptr_t)tid);
760 
761     // Removing Tid invalidates files created with it
762     if ((ssd->ftracker.fid_v1 != DCE2_SENTINEL)
763         && (ssd->ftracker.tid_v1 == tid))
764     {
765         DCE2_SmbRemoveFileTracker(ssd, &ssd->ftracker);
766     }
767 
768     if (ssd->ftrackers != nullptr)
769     {
770         DCE2_SmbFileTracker* ftracker;
771 
772         for (ftracker = (DCE2_SmbFileTracker*)DCE2_ListFirst(ssd->ftrackers);
773             ftracker != nullptr;
774             ftracker = (DCE2_SmbFileTracker*)DCE2_ListNext(ssd->ftrackers))
775         {
776             if (ftracker->tid_v1 == (int)tid)
777             {
778                 if (ssd->fapi_ftracker == ftracker)
779                     DCE2_SmbFinishFileAPI(ssd);
780 
781                 if (ssd->fb_ftracker == ftracker)
782                     DCE2_SmbFinishFileBlockVerdict(ssd);
783 
784                 DCE2_ListRemoveCurrent(ssd->ftrackers);
785                 DCE2_SmbRemoveFileTrackerFromRequestTrackers(ssd, ftracker);
786             }
787         }
788     }
789 }
790 
DCE2_SmbInsertTid(DCE2_SmbSsnData * ssd,const uint16_t tid,const bool is_ipc)791 void DCE2_SmbInsertTid(DCE2_SmbSsnData* ssd,
792     const uint16_t tid, const bool is_ipc)
793 {
794     if ( !is_ipc and
795         ssd->max_file_depth == -1 and DCE2_ScSmbFileDepth((dce2SmbProtoConf*)ssd->sd.config) == -1 )
796     {
797 	    SMB_DEBUG(dce_smb_trace, DEFAULT_TRACE_OPTION_ID, TRACE_INFO_LEVEL,
798 	        DetectionEngine::get_current_packet(), "Not inserting TID (%hu) "
799             "because it's not IPC and not inspecting normal file data.\n", tid);
800         return;
801     }
802 
803     int insert_tid = (int)tid;
804     // Set a bit so as to distinguish between IPC and non-IPC TIDs
805     if (!is_ipc)
806         insert_tid |= (1 << 16);
807 
808     if (ssd->tid == DCE2_SENTINEL)
809     {
810         ssd->tid = insert_tid;
811     }
812     else
813     {
814         if (ssd->tids == nullptr)
815         {
816             ssd->tids = DCE2_ListNew(DCE2_LIST_TYPE__SPLAYED, DCE2_SmbUidTidFidCompare,
817                 nullptr, nullptr, DCE2_LIST_FLAG__NO_DUPS);
818 
819             if (ssd->tids == nullptr)
820             {
821                 return;
822             }
823         }
824 
825         DCE2_ListInsert(ssd->tids, (void*)(uintptr_t)tid, (void*)(uintptr_t)insert_tid);
826     }
827 }
828 
829 /********************************************************************
830  * Function: DCE2_SmbInvalidShareCheck()
831  *
832  * Purpose:
833  *  Checks the share reported in a TreeConnect or TreeConnectAndX
834  *  against the invalid share list configured in the dcerpc2
835  *  configuration in snort.conf.
836  *
837  * Arguments:
838  *  DCE2_SmbSsnData * - SMB session data structure
839  *  SmbNtHdr *        - pointer to the SMB header structure
840  *  uint8_t *         - current pointer to the share to check
841  *  uint32_t          - the remaining length
842  *
843  * Returns: None
844  *  Alerts if there is an invalid share match.
845  *
846  ********************************************************************/
DCE2_SmbInvalidShareCheck(DCE2_SmbSsnData * ssd,const SmbNtHdr * smb_hdr,const uint8_t * nb_ptr,uint32_t nb_len)847 void DCE2_SmbInvalidShareCheck(DCE2_SmbSsnData* ssd,
848     const SmbNtHdr* smb_hdr, const uint8_t* nb_ptr, uint32_t nb_len)
849 {
850     DCE2_List* share_list = DCE2_ScSmbInvalidShares((dce2SmbProtoConf*)ssd->sd.config);
851     if (share_list == nullptr)
852         return;
853 
854     dce2SmbShare* smb_share;
855     for (smb_share = (dce2SmbShare*)DCE2_ListFirst(share_list);
856         smb_share != nullptr;
857         smb_share = (dce2SmbShare*)DCE2_ListNext(share_list))
858     {
859         unsigned int i;
860         const char* share_str;
861         unsigned int share_str_len;
862 
863         if (SmbUnicode(smb_hdr))
864         {
865             share_str = smb_share->unicode_str;
866             share_str_len = smb_share->unicode_str_len;
867         }
868         else
869         {
870             share_str = smb_share->ascii_str;
871             share_str_len = smb_share->ascii_str_len;
872         }
873 
874         /* Make sure we have enough data */
875         if (nb_len < share_str_len)
876             continue;
877 
878         /* Test for share match */
879         for (i = 0; i < share_str_len; i++)
880         {
881             /* All share strings should have been converted to upper case and
882              * should include null terminating bytes */
883             if ((nb_ptr[i] != share_str[i]) && (nb_ptr[i] != tolower((int)share_str[i])))
884                 break;
885         }
886 
887         if (i == share_str_len)
888         {
889             /* Should only match one share since no duplicate shares in list */
890             dce_alert(GID_DCE2, DCE2_SMB_INVALID_SHARE, (dce2CommonStats*)&dce2_smb_stats,
891                 ssd->sd);
892             break;
893         }
894     }
895 }
896 
DCE2_SmbQueueTmpFileTracker(DCE2_SmbSsnData * ssd,DCE2_SmbRequestTracker * rtracker,const uint16_t uid,const uint16_t tid)897 void DCE2_SmbQueueTmpFileTracker(DCE2_SmbSsnData* ssd,
898     DCE2_SmbRequestTracker* rtracker, const uint16_t uid, const uint16_t tid)
899 {
900     DCE2_SmbFileTracker* ftracker = (DCE2_SmbFileTracker*)
901         snort_calloc(sizeof(DCE2_SmbFileTracker));
902 
903     if (ftracker == nullptr)
904     {
905         return;
906     }
907 
908     bool is_ipc = DCE2_SmbIsTidIPC(ssd, tid);
909     if (DCE2_SmbInitFileTracker(ssd, ftracker, is_ipc, uid, tid, DCE2_SENTINEL) !=
910         DCE2_RET__SUCCESS)
911     {
912         DCE2_SmbCleanFileTracker(ftracker);
913         snort_free((void*)ftracker);
914         return;
915     }
916 
917     if (!is_ipc && (ssd->fapi_ftracker == ftracker))
918         ssd->fapi_ftracker = nullptr;
919 
920     if (rtracker->ft_queue == nullptr)
921     {
922         rtracker->ft_queue = DCE2_QueueNew(DCE2_SmbFileTrackerDataFree);
923         if (rtracker->ft_queue == nullptr)
924         {
925             DCE2_SmbCleanSessionFileTracker(ssd, ftracker);
926             return;
927         }
928     }
929 
930     if (DCE2_QueueEnqueue(rtracker->ft_queue, (void*)ftracker) != DCE2_RET__SUCCESS)
931     {
932         DCE2_SmbCleanSessionFileTracker(ssd, ftracker);
933         return;
934     }
935 }
936 
DCE2_SmbProcessRequestData(DCE2_SmbSsnData * ssd,const uint16_t fid,const uint8_t * data_ptr,uint32_t data_len,uint64_t offset)937 DCE2_Ret DCE2_SmbProcessRequestData(DCE2_SmbSsnData* ssd,
938     const uint16_t fid, const uint8_t* data_ptr, uint32_t data_len, uint64_t offset)
939 {
940     DCE2_SmbFileTracker* ftracker = DCE2_SmbGetFileTracker(ssd, fid);
941 
942     if (ftracker == nullptr)
943         return DCE2_RET__ERROR;
944 
945     // Set this in case of chained commands or reassembled packet
946     ssd->cur_rtracker->ftracker = ftracker;
947 
948     if (ftracker->is_ipc)
949     {
950         // Maximum possible fragment length is 16 bit
951         if (data_len > UINT16_MAX)
952             data_len = UINT16_MAX;
953 
954         DCE2_CoProcess(&ssd->sd, ftracker->fp_co_tracker, data_ptr, (uint16_t)data_len);
955 
956         if (!ftracker->fp_used)
957             ftracker->fp_used = true;
958     }
959     else
960     {
961         ftracker->ff_file_offset = offset;
962         DCE2_SmbProcessFileData(ssd, ftracker, data_ptr, data_len, true);
963     }
964 
965     return DCE2_RET__SUCCESS;
966 }
967 
DCE2_SmbProcessResponseData(DCE2_SmbSsnData * ssd,const uint8_t * data_ptr,uint32_t data_len)968 DCE2_Ret DCE2_SmbProcessResponseData(DCE2_SmbSsnData* ssd,
969     const uint8_t* data_ptr, uint32_t data_len)
970 {
971     DCE2_SmbFileTracker* ftracker = ssd->cur_rtracker->ftracker;
972 
973     if (ftracker == nullptr)
974         return DCE2_RET__ERROR;
975 
976     if (ftracker->is_ipc)
977     {
978         // Maximum possible fragment length is 16 bit
979         if (data_len > UINT16_MAX)
980             data_len = UINT16_MAX;
981 
982         DCE2_CoProcess(&ssd->sd, ftracker->fp_co_tracker, data_ptr, (uint16_t)data_len);
983     }
984     else
985     {
986         ftracker->ff_file_offset = ssd->cur_rtracker->file_offset;
987         DCE2_SmbProcessFileData(ssd, ftracker, data_ptr, data_len, false);
988     }
989 
990     return DCE2_RET__SUCCESS;
991 }
992 
SmbHtons(const uint16_t * ptr)993 static inline uint16_t SmbHtons(const uint16_t* ptr)
994 {
995     return alignedNtohs(ptr);
996 }
997 
998 /********************************************************************
999  * Function: DCE2_SmbInitRdata()
1000  *
1001  * Purpose:
1002  *  Initializes the reassembled packet structure for an SMB
1003  *  reassembled packet.  Uses WriteAndX and ReadAndX.
1004  *  TODO Use command that was used when reassembly occurred.
1005  *  One issue with this is that multiple different write/read
1006  *  commands can be used to write/read the full DCE/RPC
1007  *  request/response.
1008  *
1009  * Arguments:
1010  *  uint8_t * - pointer to the start of the NetBIOS header where
1011  *              data initialization should start.
1012  *  int dir   - FLAG_FROM_CLIENT or FLAG_FROM_SERVER
1013  *
1014  * Returns: None
1015  *
1016  ********************************************************************/
DCE2_SmbInitRdata(uint8_t * nb_ptr,int dir)1017 void DCE2_SmbInitRdata(uint8_t* nb_ptr, int dir)
1018 {
1019     NbssHdr* nb_hdr = (NbssHdr*)nb_ptr;
1020     SmbNtHdr* smb_hdr = (SmbNtHdr*)((uint8_t*)nb_hdr + sizeof(NbssHdr));
1021 
1022     nb_hdr->type = NBSS_SESSION_TYPE__MESSAGE;
1023     memcpy((void*)smb_hdr->smb_idf, (void*)"\xffSMB", sizeof(smb_hdr->smb_idf));
1024 
1025     if (dir == PKT_FROM_CLIENT)
1026     {
1027         SmbWriteAndXReq* writex =
1028             (SmbWriteAndXReq*)((uint8_t*)smb_hdr + sizeof(SmbNtHdr));
1029         uint16_t offset = sizeof(SmbNtHdr) + sizeof(SmbWriteAndXReq);
1030 
1031         smb_hdr->smb_com = SMB_COM_WRITE_ANDX;
1032         smb_hdr->smb_flg = 0x00;
1033 
1034         writex->smb_wct = 12;
1035         writex->smb_com2 = SMB_COM_NO_ANDX_COMMAND;
1036         writex->smb_doff = SmbHtons(&offset);
1037     }
1038     else
1039     {
1040         SmbReadAndXResp* readx =
1041             (SmbReadAndXResp*)((uint8_t*)smb_hdr + sizeof(SmbNtHdr));
1042         uint16_t offset = sizeof(SmbNtHdr) + sizeof(SmbReadAndXResp);
1043 
1044         smb_hdr->smb_com = SMB_COM_READ_ANDX;
1045         smb_hdr->smb_flg = 0x80;
1046 
1047         readx->smb_wct = 12;
1048         readx->smb_com2 = SMB_COM_NO_ANDX_COMMAND;
1049         readx->smb_doff = SmbHtons(&offset);
1050     }
1051 }
1052 
1053 /********************************************************************
1054  * Function: DCE2_SmbSetRdata()
1055  *
1056  * Purpose:
1057  *  When a reassembled packet is needed this function is called to
1058  *  fill in appropriate fields to make the reassembled packet look
1059  *  correct from an SMB standpoint.
1060  *
1061  * Arguments:
1062  *  DCE2_SmbSsnData * - the session data structure.
1063  *  uint8_t * - pointer to the start of the NetBIOS header where
1064  *              data initialization should start.
1065  *  uint16_t  - the length of the connection-oriented DCE/RPC data.
1066  *
1067  * Returns: None
1068  *
1069  ********************************************************************/
DCE2_SmbSetRdata(DCE2_SmbSsnData * ssd,uint8_t * nb_ptr,uint16_t co_len)1070 void DCE2_SmbSetRdata(DCE2_SmbSsnData* ssd, uint8_t* nb_ptr, uint16_t co_len)
1071 {
1072     NbssHdr* nb_hdr = (NbssHdr*)nb_ptr;
1073     SmbNtHdr* smb_hdr = (SmbNtHdr*)((uint8_t*)nb_hdr + sizeof(NbssHdr));
1074     uint16_t uid = (ssd->cur_rtracker == nullptr) ? 0 : ssd->cur_rtracker->uid;
1075     uint16_t tid = (ssd->cur_rtracker == nullptr) ? 0 : ssd->cur_rtracker->tid;
1076     DCE2_SmbFileTracker* ftracker = (ssd->cur_rtracker == nullptr) ? nullptr :
1077         ssd->cur_rtracker->ftracker;
1078 
1079     smb_hdr->smb_uid = SmbHtons((const uint16_t*)&uid);
1080     smb_hdr->smb_tid = SmbHtons((const uint16_t*)&tid);
1081 
1082     if ( DetectionEngine::get_current_packet()->is_from_client() )
1083     {
1084         SmbWriteAndXReq* writex =
1085             (SmbWriteAndXReq*)((uint8_t*)smb_hdr + sizeof(SmbNtHdr));
1086         uint32_t nb_len = sizeof(SmbNtHdr) + sizeof(SmbWriteAndXReq) + co_len;
1087 
1088         /* The data will get truncated anyway since we can only fit
1089          * 64K in the reassembly buffer */
1090         if (nb_len > UINT16_MAX)
1091             nb_len = UINT16_MAX;
1092 
1093         nb_hdr->length = htons((uint16_t)nb_len);
1094 
1095         if ((ftracker != nullptr) && (ftracker->fid_v1 > 0))
1096         {
1097             uint16_t fid = (uint16_t)ftracker->fid_v1;
1098             writex->smb_fid = SmbHtons(&fid);
1099         }
1100         else
1101         {
1102             writex->smb_fid = 0;
1103         }
1104 
1105         writex->smb_countleft = SmbHtons(&co_len);
1106         writex->smb_dsize = SmbHtons(&co_len);
1107         writex->smb_bcc = SmbHtons(&co_len);
1108     }
1109     else
1110     {
1111         SmbReadAndXResp* readx =
1112             (SmbReadAndXResp*)((uint8_t*)smb_hdr + sizeof(SmbNtHdr));
1113         uint32_t nb_len = sizeof(SmbNtHdr) + sizeof(SmbReadAndXResp) + co_len;
1114 
1115         /* The data will get truncated anyway since we can only fit
1116          * 64K in the reassembly buffer */
1117         if (nb_len > UINT16_MAX)
1118             nb_len = UINT16_MAX;
1119 
1120         nb_hdr->length = htons((uint16_t)nb_len);
1121 
1122         readx->smb_remaining = SmbHtons(&co_len);
1123         readx->smb_dsize = SmbHtons(&co_len);
1124         readx->smb_bcc = SmbHtons(&co_len);
1125     }
1126 }
1127 
DCE2_SmbGetRpkt(DCE2_SmbSsnData * ssd,const uint8_t ** data,uint32_t * data_len,DCE2_RpktType rtype)1128 Packet* DCE2_SmbGetRpkt(DCE2_SmbSsnData* ssd,
1129     const uint8_t** data, uint32_t* data_len, DCE2_RpktType rtype)
1130 {
1131     if ((ssd == nullptr) || (data == nullptr) || (*data == nullptr)
1132         || (data_len == nullptr) || (*data_len == 0))
1133         return nullptr;
1134 
1135     Packet* rpkt = DCE2_GetRpkt(DetectionEngine::get_current_packet(), rtype, *data, *data_len);
1136 
1137     if ( !rpkt )
1138         return nullptr;
1139 
1140     *data = rpkt->data;
1141     *data_len = rpkt->dsize;
1142 
1143     uint16_t header_len;
1144     switch (rtype)
1145     {
1146     case DCE2_RPKT_TYPE__SMB_TRANS:
1147         if (DCE2_SmbType() == SMB_TYPE__REQUEST)
1148             header_len = DCE2_MOCK_HDR_LEN__SMB_CLI;
1149         else
1150             header_len = DCE2_MOCK_HDR_LEN__SMB_SRV;
1151         DCE2_SmbSetRdata(ssd, const_cast<uint8_t*>(rpkt->data),
1152             (uint16_t)(rpkt->dsize - header_len));
1153         dce2_move(*data, *data_len, header_len);
1154         break;
1155     case DCE2_RPKT_TYPE__SMB_SEG:
1156     default:
1157         break;
1158     }
1159 
1160     return rpkt;
1161 }
1162 
1163 /********************************************************************
1164  * Function: DCE2_SmbHandleSegmentation()
1165  *
1166  * Wrapper around DCE2_HandleSegmentation() to allocate a new
1167  * buffer object if necessary.
1168  *
1169  * Arguments:
1170  *  DCE2_SmbBuffer **
1171  *      Pointer to pointer of buffer to add data to.  If null
1172  *      a new buffer will be allocated.
1173  *  uint8_t *
1174  *      Pointer to the current data cursor in packet.
1175  *  uint32_t
1176  *      Length of data to add to buffer.
1177  *  uint32_t
1178  *      The minimum allocation size so that small allocations
1179  *      aren't consistently done.
1180  *
1181  * Returns:
1182  *  DCE2_Ret
1183  *      DCE2_RET__ERROR if an error occurred.  Nothing can
1184  *          be trusted.
1185  *      DCE2_RET__SUCCESS if data was successfully added.
1186  *
1187  ********************************************************************/
DCE2_SmbHandleSegmentation(DCE2_Buffer ** buf,const uint8_t * data_ptr,uint32_t add_len,uint32_t alloc_size)1188 DCE2_Ret DCE2_SmbHandleSegmentation(DCE2_Buffer** buf,
1189     const uint8_t* data_ptr, uint32_t add_len, uint32_t alloc_size)
1190 {
1191     if (buf == nullptr)
1192     {
1193         return DCE2_RET__ERROR;
1194     }
1195 
1196     if (*buf == nullptr)
1197     {
1198         /* No initial size or min alloc size */
1199         *buf = DCE2_BufferNew(alloc_size, alloc_size);
1200     }
1201 
1202     DCE2_Ret status = DCE2_BufferAddData(*buf, data_ptr, add_len,
1203         DCE2_BufferLength(*buf), DCE2_BUFFER_MIN_ADD_FLAG__IGNORE);
1204 
1205     return status;
1206 }
1207 
1208 /********************************************************************
1209  * Function: DCE2_SmbIsSegBuffer()
1210  *
1211  * Purpose:
1212  *  Determines whether the pointer passed in lies within one of the
1213  *  segmentation buffers or not.
1214  *
1215  * Arguments:
1216  *  DCE2_SmbSsnData *
1217  *      Pointer to SMB session data.
1218  *
1219  * Returns:
1220  *  bool  -  True is the pointer lies within one of the segmentation
1221  *           buffers.
1222  *           False if it doesn't.
1223  *
1224  ********************************************************************/
DCE2_SmbIsSegBuffer(DCE2_SmbSsnData * ssd,const uint8_t * ptr)1225 bool DCE2_SmbIsSegBuffer(DCE2_SmbSsnData* ssd, const uint8_t* ptr)
1226 {
1227     DCE2_Buffer* seg_buf;
1228 
1229     if ( DetectionEngine::get_current_packet()->is_from_server() )
1230         seg_buf = ssd->srv_seg;
1231     else
1232         seg_buf = ssd->cli_seg;
1233 
1234     if (DCE2_BufferIsEmpty(seg_buf))
1235         return false;
1236 
1237     /* See if we're looking at a segmentation buffer */
1238     if ((ptr < DCE2_BufferData(seg_buf)) ||
1239         (ptr > (DCE2_BufferData(seg_buf) + DCE2_BufferLength(seg_buf))))
1240     {
1241         return false;
1242     }
1243 
1244     return true;
1245 }
1246 
1247 /********************************************************************
1248  * Function: DCE2_SmbSegAlert()
1249  *
1250  * Purpose:
1251  *  To create a reassembled packet using the data in one of the
1252  *  segmentation buffers in order to generate an alert with the
1253  *  correct, or more complete data.
1254  *
1255  * Arguments:
1256  *  DCE2_SmbSsnData * - Pointer to SMB session data.
1257  *  rule_id -  rule id .
1258  *
1259  * Returns: None
1260  *
1261  ********************************************************************/
DCE2_SmbSegAlert(DCE2_SmbSsnData * ssd,uint32_t rule_id)1262 void DCE2_SmbSegAlert(DCE2_SmbSsnData* ssd, uint32_t rule_id)
1263 {
1264     DCE2_Buffer* buf;
1265 
1266     if ( DetectionEngine::get_current_packet()->is_from_client() )
1267         buf = ssd->cli_seg;
1268     else
1269         buf = ssd->srv_seg;
1270 
1271     /* This should be called from the desegmentation code. */
1272     if (DCE2_BufferIsEmpty(buf))
1273         return;
1274 
1275     const uint8_t* data_ptr = DCE2_BufferData(buf);
1276     uint32_t data_len = DCE2_BufferLength(buf);
1277 
1278     Packet* rpkt = DCE2_SmbGetRpkt(ssd, &data_ptr, &data_len, DCE2_RPKT_TYPE__SMB_SEG);
1279     if (rpkt == nullptr)
1280         return;
1281 
1282     dce_alert(GID_DCE2, rule_id, (dce2CommonStats*)&dce2_smb_stats, ssd->sd);
1283 }
1284 
DCE2_SmbResetFileChunks(DCE2_SmbFileTracker * ftracker)1285 static void DCE2_SmbResetFileChunks(DCE2_SmbFileTracker* ftracker)
1286 {
1287     if (ftracker == nullptr)
1288         return;
1289 
1290     DCE2_ListDestroy(ftracker->ff_file_chunks);
1291     ftracker->ff_file_chunks = nullptr;
1292     ftracker->ff_bytes_queued = 0;
1293 }
1294 
DCE2_SmbAbortFileAPI(DCE2_SmbSsnData * ssd)1295 void DCE2_SmbAbortFileAPI(DCE2_SmbSsnData* ssd)
1296 {
1297     DCE2_SmbResetFileChunks(ssd->fapi_ftracker);
1298     ssd->fapi_ftracker = nullptr;
1299 }
1300 
DCE2_get_main_file_context()1301 static FileContext* DCE2_get_main_file_context()
1302 {
1303     FileFlows* file_flows = FileFlows::get_file_flows(DetectionEngine::get_current_packet()->flow);
1304     if (file_flows)
1305         return file_flows->get_current_file_context();
1306     else
1307         return nullptr;
1308 }
1309 
DCE2_get_file_verdict()1310 FileVerdict DCE2_get_file_verdict()
1311 {
1312     FileContext* file = DCE2_get_main_file_context();
1313     if ( !file )
1314         return FILE_VERDICT_UNKNOWN;
1315     return file->verdict;
1316 }
1317 
DCE2_SmbInitDeletePdu()1318 void DCE2_SmbInitDeletePdu()
1319 {
1320     NbssHdr* nb_hdr = (NbssHdr*)dce2_smb_delete_pdu;
1321     SmbNtHdr* smb_hdr = (SmbNtHdr*)((uint8_t*)nb_hdr + sizeof(*nb_hdr));
1322     SmbDeleteReq* del_req = (SmbDeleteReq*)((uint8_t*)smb_hdr + sizeof(*smb_hdr));
1323     uint8_t* del_req_fmt = (uint8_t*)del_req + sizeof(*del_req);
1324     uint16_t smb_flg2 = 0x4001;
1325     uint16_t search_attrs = 0x0006;
1326 
1327     memset(dce2_smb_delete_pdu, 0, sizeof(dce2_smb_delete_pdu));
1328 
1329     nb_hdr->type = 0;
1330     nb_hdr->flags = 0;
1331 
1332     memcpy((void*)smb_hdr->smb_idf, (void*)"\xffSMB", sizeof(smb_hdr->smb_idf));
1333     smb_hdr->smb_com = SMB_COM_DELETE;
1334     smb_hdr->smb_status.nt_status = 0;
1335     //smb_hdr->smb_flg = 0x18;
1336     smb_hdr->smb_flg = 0;
1337     smb_hdr->smb_flg2 = SmbHtons(&smb_flg2);
1338     smb_hdr->smb_tid = 0;   // needs to be set before injected
1339     smb_hdr->smb_pid = 777;
1340     smb_hdr->smb_uid = 0;   // needs to be set before injected
1341     smb_hdr->smb_mid = 777;
1342 
1343     del_req->smb_wct = 1;
1344     del_req->smb_search_attrs = SmbHtons(&search_attrs);
1345     *del_req_fmt = SMB_FMT__ASCII;
1346 }
1347 
DCE2_SmbInjectDeletePdu(DCE2_SmbFileTracker * ftracker)1348 static void DCE2_SmbInjectDeletePdu(DCE2_SmbFileTracker* ftracker)
1349 {
1350     Packet* inject_pkt = DetectionEngine::get_current_wire_packet();
1351     Packet* p = DetectionEngine::get_current_packet();
1352 
1353     if ( inject_pkt->flow != p->flow )
1354         return;
1355 
1356     NbssHdr* nb_hdr = (NbssHdr*)dce2_smb_delete_pdu;
1357     SmbNtHdr* smb_hdr = (SmbNtHdr*)((uint8_t*)nb_hdr + sizeof(*nb_hdr));
1358     SmbDeleteReq* del_req = (SmbDeleteReq*)((uint8_t*)smb_hdr + sizeof(*smb_hdr));
1359     char* del_filename = (char*)((uint8_t*)del_req + sizeof(*del_req) + 1);
1360     FileCharEncoding encoding = get_character_encoding(ftracker->file_name,
1361         ftracker->file_name_size);
1362     uint16_t file_name_len;
1363 
1364     if (encoding == SNORT_CHAR_ENCODING_UTF_16LE)
1365     {
1366         file_name_len = ftracker->file_name_size - UTF_16_LE_BOM_LEN + 2;
1367         uint16_t smb_flg2 = 0xC001;
1368         smb_hdr->smb_flg2 = SmbHtons(&smb_flg2);
1369     }
1370     else
1371     {
1372         file_name_len = ftracker->file_name_size + 1;
1373     }
1374 
1375     nb_hdr->length = htons(sizeof(*smb_hdr) + sizeof(*del_req) + 1 + file_name_len);
1376     uint32_t len = ntohs(nb_hdr->length) + sizeof(*nb_hdr);
1377     smb_hdr->smb_tid = SmbHtons(&ftracker->tid_v1);
1378     smb_hdr->smb_uid = SmbHtons(&ftracker->uid_v1);
1379     del_req->smb_bcc = 1 + file_name_len;
1380     memcpy(del_filename, ftracker->file_name + UTF_16_LE_BOM_LEN, file_name_len);
1381 
1382     p->active->inject_data(inject_pkt, 0, (uint8_t*)nb_hdr, len);
1383 }
1384 
DCE2_SmbLookupFileVerdict()1385 static FileVerdict DCE2_SmbLookupFileVerdict()
1386 {
1387     FileContext* file = DCE2_get_main_file_context();
1388 
1389     if ( !file )
1390         return FILE_VERDICT_UNKNOWN;
1391 
1392     FileVerdict verdict = file->verdict;
1393 
1394     if (verdict == FILE_VERDICT_PENDING)
1395         verdict = file->file_signature_lookup(DetectionEngine::get_current_packet());
1396 
1397     return verdict;
1398 }
1399 
DCE2_SmbFinishFileBlockVerdict(DCE2_SmbSsnData * ssd)1400 static void DCE2_SmbFinishFileBlockVerdict(DCE2_SmbSsnData* ssd)
1401 {
1402     FileVerdict verdict = DCE2_SmbLookupFileVerdict();
1403     if ((verdict == FILE_VERDICT_BLOCK) || (verdict == FILE_VERDICT_REJECT))
1404     {
1405         DCE2_SmbInjectDeletePdu(ssd->fb_ftracker);
1406         DetectionEngine::get_current_packet()->active->set_drop_reason("smb");
1407     }
1408 
1409     ssd->fb_ftracker = nullptr;
1410     ssd->block_pdus = false;
1411 }
1412 
DCE2_SmbFinishFileAPI(DCE2_SmbSsnData * ssd)1413 static void DCE2_SmbFinishFileAPI(DCE2_SmbSsnData* ssd)
1414 {
1415     Packet* p = DetectionEngine::get_current_packet();
1416     DCE2_SmbFileTracker* ftracker = ssd->fapi_ftracker;
1417 
1418     if (ftracker == nullptr)
1419         return;
1420 
1421     FileFlows* file_flows = FileFlows::get_file_flows(p->flow);
1422     bool upload = (ftracker->ff_file_direction == DCE2_SMB_FILE_DIRECTION__UPLOAD);
1423 
1424     if (get_file_processed_size(p->flow) != 0)
1425     {
1426         // Never knew the size of the file so never knew when to tell the
1427         // fileAPI the upload/download was finished.
1428         if ((ftracker->ff_file_size == 0)
1429             && (ftracker->ff_bytes_processed != 0))
1430         {
1431             if (file_flows->file_process(p, nullptr, 0, SNORT_FILE_END, upload,
1432                 ftracker->file_name_hash))
1433             {
1434                 if (upload)
1435                 {
1436                     FileVerdict verdict = DCE2_get_file_verdict();
1437 
1438                     if ((verdict == FILE_VERDICT_BLOCK) || (verdict == FILE_VERDICT_REJECT))
1439                     {
1440                         ssd->fb_ftracker = ftracker;
1441                         if (PacketTracer::is_active())
1442                             PacketTracer::log("Dce2_smb: smb file verdict %s\n",
1443                                 verdict == FILE_VERDICT_BLOCK ? "block" : "reject");
1444                     }
1445                 }
1446             }
1447             dce2_smb_stats.smb_files_processed++;
1448         }
1449     }
1450 
1451     ssd->fapi_ftracker = nullptr;
1452 }
1453 
DCE2_SmbFileAPIProcess(DCE2_SmbSsnData * ssd,DCE2_SmbFileTracker * ftracker,const uint8_t * data_ptr,uint32_t data_len,bool upload)1454 static DCE2_Ret DCE2_SmbFileAPIProcess(DCE2_SmbSsnData* ssd,
1455     DCE2_SmbFileTracker* ftracker, const uint8_t* data_ptr,
1456     uint32_t data_len, bool upload)
1457 {
1458     FilePosition position;
1459 
1460     if (ssd->fb_ftracker && (ssd->fb_ftracker != ftracker))
1461         return DCE2_RET__SUCCESS;
1462 
1463     // Trim data length if it exceeds the maximum file depth
1464     if ((ssd->max_file_depth != 0)
1465         && (ftracker->ff_bytes_processed + data_len) > (uint64_t)ssd->max_file_depth)
1466         data_len = ssd->max_file_depth - ftracker->ff_bytes_processed;
1467 
1468     if (ftracker->ff_file_size == 0)
1469     {
1470         // Don't know the file size.
1471         if ((ftracker->ff_bytes_processed == 0) && (ssd->max_file_depth != 0)
1472             && (data_len == (uint64_t)ssd->max_file_depth))
1473             position = SNORT_FILE_FULL;
1474         else if (ftracker->ff_bytes_processed == 0)
1475             position = SNORT_FILE_START;
1476         else if ((ssd->max_file_depth != 0)
1477             && ((ftracker->ff_bytes_processed + data_len) == (uint64_t)ssd->max_file_depth))
1478             position = SNORT_FILE_END;
1479         else
1480             position = SNORT_FILE_MIDDLE;
1481     }
1482     else
1483     {
1484         if ((ftracker->ff_bytes_processed == 0)
1485             && ((data_len == ftracker->ff_file_size)
1486             || ((ssd->max_file_depth != 0) && (data_len == (uint64_t)ssd->max_file_depth))))
1487             position = SNORT_FILE_FULL;
1488         else if (ftracker->ff_bytes_processed == 0)
1489             position = SNORT_FILE_START;
1490         else if (((ftracker->ff_bytes_processed + data_len) >= ftracker->ff_file_size)
1491             || ((ssd->max_file_depth != 0)
1492             && ((ftracker->ff_bytes_processed + data_len) == (uint64_t)ssd->max_file_depth)))
1493             position = SNORT_FILE_END;
1494         else
1495             position = SNORT_FILE_MIDDLE;
1496     }
1497 
1498     Packet* p = DetectionEngine::get_current_packet();
1499     FileFlows* file_flows = FileFlows::get_file_flows(p->flow);
1500 
1501     if (!file_flows)
1502         return DCE2_RET__ERROR;
1503 
1504     if (!file_flows->file_process(p, data_ptr, (int)data_len, position, upload,
1505         ftracker->file_name_hash))
1506     {
1507 	    SMB_DEBUG(dce_smb_trace, DEFAULT_TRACE_OPTION_ID, TRACE_ERROR_LEVEL,
1508 	        p, "File API returned FAILURE for (0x%02X) %s\n",
1509 	        ftracker->fid_v1, upload ? "UPLOAD" : "DOWNLOAD");
1510 
1511         // Failure.  Abort tracking this file under file API
1512         return DCE2_RET__ERROR;
1513     }
1514     else
1515     {
1516         if (((position == SNORT_FILE_START) || (position == SNORT_FILE_FULL))
1517             && (ftracker->file_name_size != 0))
1518         {
1519             file_flows->set_file_name((uint8_t*)ftracker->file_name, ftracker->file_name_size);
1520         }
1521 
1522         if ((position == SNORT_FILE_FULL) || (position == SNORT_FILE_END))
1523         {
1524             if (upload)
1525             {
1526                 FileVerdict verdict = DCE2_get_file_verdict();
1527 
1528                 if ((verdict == FILE_VERDICT_BLOCK) || (verdict == FILE_VERDICT_REJECT)
1529                     || (verdict == FILE_VERDICT_PENDING))
1530                 {
1531                     ssd->fb_ftracker = ftracker;
1532                     if (verdict != FILE_VERDICT_PENDING and PacketTracer::is_active())
1533                     {
1534                         PacketTracer::log("Dce2_smb: smb file verdict %s\n",
1535                             verdict == FILE_VERDICT_BLOCK ? "block" : "reject");
1536                     }
1537                 }
1538             }
1539             ftracker->ff_sequential_only = false;
1540 
1541             dce2_smb_stats.smb_files_processed++;
1542             return DCE2_RET__FULL;
1543         }
1544     }
1545 
1546     return DCE2_RET__SUCCESS;
1547 }
1548 
DCE2_SmbFileOffsetCompare(const void * a,const void * b)1549 static int DCE2_SmbFileOffsetCompare(const void* a, const void* b)
1550 {
1551     const DCE2_SmbFileChunk* x = (const DCE2_SmbFileChunk*)a;
1552     const DCE2_SmbFileChunk* y = (const DCE2_SmbFileChunk*)b;
1553 
1554     if (x->offset > y->offset)
1555         return 1;
1556     if (x->offset < y->offset)
1557         return -1;
1558 
1559     return 0;
1560 }
1561 
DCE2_SmbFileChunkFree(void * data)1562 static void DCE2_SmbFileChunkFree(void* data)
1563 {
1564     DCE2_SmbFileChunk* fc = (DCE2_SmbFileChunk*)data;
1565 
1566     if (fc == nullptr)
1567         return;
1568 
1569     if (fc->data != nullptr)
1570         snort_free((void*)fc->data);
1571 
1572     snort_free((void*)fc);
1573 }
1574 
DCE2_SmbHandleOutOfOrderFileData(DCE2_SmbSsnData * ssd,DCE2_SmbFileTracker * ftracker,const uint8_t * data_ptr,uint32_t data_len,bool upload)1575 static DCE2_Ret DCE2_SmbHandleOutOfOrderFileData(DCE2_SmbSsnData* ssd,
1576     DCE2_SmbFileTracker* ftracker, const uint8_t* data_ptr,
1577     uint32_t data_len, bool upload)
1578 {
1579     if (ftracker->ff_file_offset == ftracker->ff_bytes_processed)
1580     {
1581         uint64_t initial_offset = ftracker->ff_file_offset;
1582         uint64_t next_offset = initial_offset + data_len;
1583         DCE2_SmbFileChunk* file_chunk = (DCE2_SmbFileChunk*)DCE2_ListFirst(
1584             ftracker->ff_file_chunks);
1585         DCE2_Ret ret = DCE2_SmbFileAPIProcess(ssd, ftracker, data_ptr, data_len, upload);
1586 
1587         ftracker->ff_bytes_processed += data_len;
1588         ftracker->ff_file_offset = ftracker->ff_bytes_processed;
1589 
1590         if (ret != DCE2_RET__SUCCESS)
1591             return ret;
1592 
1593         // Should already be chunks in here if we came into this function
1594         // with an in order chunk, but check just in case.
1595         if (file_chunk == nullptr)
1596             return DCE2_RET__ERROR;
1597 
1598         while (file_chunk != nullptr)
1599         {
1600             if (file_chunk->offset > next_offset)
1601                 break;
1602 
1603             if (file_chunk->offset == next_offset)
1604             {
1605                 ret = DCE2_SmbFileAPIProcess(ssd, ftracker,
1606                     file_chunk->data, file_chunk->length, upload);
1607 
1608                 ftracker->ff_bytes_processed += file_chunk->length;
1609                 ftracker->ff_file_offset = ftracker->ff_bytes_processed;
1610 
1611                 if (ret != DCE2_RET__SUCCESS)
1612                     return ret;
1613 
1614                 next_offset = file_chunk->offset + file_chunk->length;
1615             }
1616 
1617             ftracker->ff_bytes_queued -= file_chunk->length;
1618             DCE2_ListRemoveCurrent(ftracker->ff_file_chunks);
1619 
1620             file_chunk = (DCE2_SmbFileChunk*)DCE2_ListNext(ftracker->ff_file_chunks);
1621         }
1622 
1623         if (initial_offset == 0)
1624             DCE2_SmbResetFileChunks(ftracker);
1625     }
1626     else
1627     {
1628         if (ftracker->ff_file_chunks == nullptr)
1629         {
1630             ftracker->ff_file_chunks = DCE2_ListNew(DCE2_LIST_TYPE__SORTED,
1631                 DCE2_SmbFileOffsetCompare, DCE2_SmbFileChunkFree,
1632                 nullptr, DCE2_LIST_FLAG__NO_DUPS);
1633 
1634             if (ftracker->ff_file_chunks == nullptr)
1635                 return DCE2_RET__ERROR;
1636         }
1637 
1638         DCE2_SmbFileChunk* file_chunk = (DCE2_SmbFileChunk*)snort_calloc(
1639             sizeof(DCE2_SmbFileChunk));
1640         file_chunk->data = (uint8_t*)snort_calloc(data_len);
1641 
1642         file_chunk->offset = ftracker->ff_file_offset;
1643         file_chunk->length = data_len;
1644         memcpy(file_chunk->data, data_ptr, data_len);
1645         ftracker->ff_bytes_queued += data_len;
1646 
1647         DCE2_Ret ret;
1648         if ((ret = DCE2_ListInsert(ftracker->ff_file_chunks,
1649                 (void*)file_chunk, (void*)file_chunk)) != DCE2_RET__SUCCESS)
1650         {
1651             snort_free((void*)file_chunk->data);
1652             snort_free((void*)file_chunk);
1653 
1654             if (ret != DCE2_RET__DUPLICATE)
1655                 return DCE2_RET__ERROR;
1656         }
1657     }
1658 
1659     return DCE2_RET__SUCCESS;
1660 }
1661 
1662 /********************************************************************
1663  * Function: DCE2_SmbProcessFileData()
1664  *
1665  * Purpose:
1666  *  Processes regular file data send via reads/writes.  Sends
1667  *  data to the file API for type id and signature and sets the
1668  *  file data ptr for rule inspection.
1669  *
1670  * Arguments:
1671  *  DCE2_SmbSsnData *      - pointer to SMB session data
1672  *  DCE2_SmbFileTracker *  - pointer to file tracker
1673  *  const uint8_t *        - pointer to file data
1674  *  uint32_t               - length of file data
1675  *  bool                   - whether it's an upload (true) or
1676  *                           download (false)
1677  *
1678  * Returns: None
1679  *
1680  ********************************************************************/
DCE2_SmbProcessFileData(DCE2_SmbSsnData * ssd,DCE2_SmbFileTracker * ftracker,const uint8_t * data_ptr,uint32_t data_len,bool upload)1681 void DCE2_SmbProcessFileData(DCE2_SmbSsnData* ssd,
1682     DCE2_SmbFileTracker* ftracker, const uint8_t* data_ptr,
1683     uint32_t data_len, bool upload)
1684 {
1685     bool cur_upload = DCE2_SmbFileUpload(ftracker->ff_file_direction) ? true : false;
1686     int64_t file_data_depth = DCE2_ScSmbFileDepth((dce2SmbProtoConf*)ssd->sd.config);
1687 
1688     if (data_len == 0)
1689         return;
1690 
1691     // Account for wrapping.  Not likely but just in case.
1692     if ((ftracker->ff_bytes_processed + data_len) < ftracker->ff_bytes_processed)
1693     {
1694         DCE2_SmbRemoveFileTracker(ssd, ftracker);
1695         return;
1696     }
1697 
1698     if ((ftracker->ff_bytes_processed == 0)
1699         && DCE2_SmbFileDirUnknown(ftracker->ff_file_direction))
1700     {
1701         ftracker->ff_file_direction =
1702             upload ? DCE2_SMB_FILE_DIRECTION__UPLOAD : DCE2_SMB_FILE_DIRECTION__DOWNLOAD;
1703     }
1704     else if (cur_upload != upload)
1705     {
1706         if (cur_upload)
1707         {
1708             // Went from writing to reading.  Ignore the read.
1709             return;
1710         }
1711 
1712         // Went from reading to writing.  Consider the transfer done
1713         // and remove the file tracker.
1714         DCE2_SmbRemoveFileTracker(ssd, ftracker);
1715         return;
1716     }
1717 
1718     if ((file_data_depth != -1) &&
1719         ((ftracker->ff_file_offset == ftracker->ff_bytes_processed) &&
1720         ((file_data_depth == 0) || (ftracker->ff_bytes_processed < (uint64_t)file_data_depth))))
1721     {
1722         set_file_data(data_ptr, (data_len > UINT16_MAX) ? UINT16_MAX : (uint16_t)data_len);
1723         DCE2_FileDetect();
1724     }
1725 
1726     if (ftracker == ssd->fapi_ftracker)
1727     {
1728         DCE2_Ret ret;
1729 
1730         if ((ftracker->ff_file_offset != ftracker->ff_bytes_processed)
1731             || !DCE2_ListIsEmpty(ftracker->ff_file_chunks))
1732         {
1733             if ((ssd->max_file_depth != 0)
1734                 && (ftracker->ff_file_offset >= (uint64_t)ssd->max_file_depth))
1735             {
1736                 // If the offset is beyond the max file depth, ignore it.
1737                 return;
1738             }
1739             else if (upload && (data_len == 1)
1740                 && (ftracker->ff_file_offset > ftracker->ff_bytes_processed))
1741             {
1742                 // Sometimes a write one byte is done at a high offset, I'm
1743                 // guessing to make sure the system has sufficient disk
1744                 // space to complete the full write.  Ignore it because it
1745                 // will likely be overwritten.
1746                 return;
1747             }
1748 
1749             if ((ftracker->ff_file_offset == 0) && (ftracker->ff_bytes_processed != 0))
1750             {
1751                 // Sometimes initial reads/writes are out of order to get file info
1752                 // such as an icon, then proceed to write in order.  Usually the
1753                 // first read/write is at offset 0, then the next ones are somewhere
1754                 // off in the distance.  Reset and continue on below.
1755                 DCE2_SmbResetFileChunks(ftracker);
1756                 ftracker->ff_bytes_processed = 0;
1757             }
1758             else if (ftracker->ff_file_offset < ftracker->ff_bytes_processed)
1759             {
1760 		        SMB_DEBUG(dce_smb_trace, DEFAULT_TRACE_OPTION_ID, TRACE_INFO_LEVEL,
1761 	                DetectionEngine::get_current_packet(), "File offset %" PRIu64 " is less than bytes processed %"
1762 		            PRIu64 " - aborting.\n", ftracker->ff_file_offset,
1763 		            ftracker->ff_bytes_processed);
1764 
1765                 DCE2_SmbAbortFileAPI(ssd);
1766                 DCE2_SmbSetNewFileAPIFileTracker(ssd);
1767                 return;
1768             }
1769             else
1770             {
1771                 ret = DCE2_SmbHandleOutOfOrderFileData(ssd, ftracker, data_ptr, data_len, upload);
1772                 if (ret != DCE2_RET__SUCCESS)
1773                 {
1774                     DCE2_SmbAbortFileAPI(ssd);
1775                     DCE2_SmbSetNewFileAPIFileTracker(ssd);
1776                 }
1777                 return;
1778             }
1779         }
1780 
1781         ret = DCE2_SmbFileAPIProcess(ssd, ftracker, data_ptr, data_len, upload);
1782 
1783         ftracker->ff_bytes_processed += data_len;
1784         ftracker->ff_file_offset = ftracker->ff_bytes_processed;
1785 
1786         if (ret != DCE2_RET__SUCCESS)
1787         {
1788             DCE2_SmbAbortFileAPI(ssd);
1789             DCE2_SmbSetNewFileAPIFileTracker(ssd);
1790         }
1791     }
1792     else
1793     {
1794         if (ftracker->ff_file_offset == ftracker->ff_bytes_processed)
1795         {
1796             ftracker->ff_bytes_processed += data_len;
1797             ftracker->ff_file_offset = ftracker->ff_bytes_processed;
1798         }
1799 
1800         if ((file_data_depth == -1)
1801             || ((file_data_depth != 0)
1802             && (ftracker->ff_bytes_processed >= (uint64_t)file_data_depth)))
1803         {
1804             // Bytes processed is at or beyond file data depth - finished.
1805             DCE2_SmbRemoveFileTracker(ssd, ftracker);
1806             return;
1807         }
1808     }
1809 }
1810 
DCE2_FileDetect()1811 void DCE2_FileDetect()
1812 {
1813     Packet* top_pkt = DetectionEngine::get_current_packet();
1814     DetectionEngine::detect(top_pkt);
1815     dce2_detected = 1;
1816 }
1817 
DCE2_SmbSetNewFileAPIFileTracker(DCE2_SmbSsnData * ssd)1818 static void DCE2_SmbSetNewFileAPIFileTracker(DCE2_SmbSsnData* ssd)
1819 {
1820     assert(ssd);
1821     DCE2_SmbFileTracker* ftracker = &ssd->ftracker;
1822 
1823     while (ftracker != nullptr)
1824     {
1825         if ((ftracker != ssd->fapi_ftracker) && (ftracker->fid_v1 != DCE2_SENTINEL)
1826             && !ftracker->is_ipc && ftracker->ff_sequential_only
1827             && (ftracker->ff_bytes_processed == 0))
1828         {
1829             break;
1830         }
1831 
1832         if (ftracker == &ssd->ftracker)
1833             ftracker = (DCE2_SmbFileTracker*)DCE2_ListFirst(ssd->ftrackers);
1834         else
1835             ftracker = (DCE2_SmbFileTracker*)DCE2_ListNext(ssd->ftrackers);
1836     }
1837     ssd->fapi_ftracker = ftracker;
1838 }
1839 
DCE2_Update_Ftracker_from_ReqTracker(DCE2_SmbFileTracker * ftracker,DCE2_SmbRequestTracker * cur_rtracker)1840 void DCE2_Update_Ftracker_from_ReqTracker(DCE2_SmbFileTracker* ftracker,
1841     DCE2_SmbRequestTracker* cur_rtracker)
1842 {
1843     ftracker->file_name = cur_rtracker->file_name;
1844     ftracker->file_name_size = cur_rtracker->file_name_size;
1845     ftracker->file_name_hash = str_to_hash(
1846         (const uint8_t*)cur_rtracker->file_name, cur_rtracker->file_name_size);
1847     cur_rtracker->file_name = nullptr;
1848     cur_rtracker->file_name_size = 0;
1849 }
1850 
1851