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