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_commands.cc author Maya Dagon <mdagon@cisco.com>
20 // based on work by Todd Wease
21
22 // Smb commands processing
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include "dce_smb_commands.h"
29
30 #include "utils/util.h"
31
32 #include "dce_smb_module.h"
33 #include "dce_smb_transaction_utils.h"
34
35 using namespace snort;
36
37 #define SMB_DIALECT_NT_LM_012 "NT LM 0.12" // NT LAN Manager
38
39 #define SERVICE_0 (0) // IPC start
40 #define SERVICE_1 (SERVICE_0+4) // DISK start
41 #define SERVICE_FS (SERVICE_1+3) // Failure
42 #define SERVICE_IPC (SERVICE_FS+1) // IPC service
43 #define SERVICE_DISK (SERVICE_FS+2) // DISK service
44
45 #define SHARE_0 (0)
46 #define SHARE_FS (SHARE_0+5)
47 #define SHARE_IPC (SHARE_FS+1)
48
49 #define OS_0 (0) // "Windows" start
50 #define OS_1 (OS_0+ 8) // Windows 2000 and XP server
51 #define OS_2 (OS_1+ 4) // Windows 2000 and XP client
52 #define OS_3 (OS_2+ 5) // "Server", 2003, 2008R2, 2008
53 #define OS_4 (OS_3+20) // Windows Vista
54 #define OS_5 (OS_4 +5) // Windows 7
55 #define OS_6 (OS_5 +1) // Windows NT
56 #define OS_7 (OS_6 +2) // Windows 98
57 #define OS_FS (OS_7+ 3) // Failure state
58 #define OS_WIN2000 (OS_FS+1)
59 #define OS_WINXP (OS_FS+2)
60 #define OS_WIN2003 (OS_FS+3)
61 #define OS_WINVISTA (OS_FS+4)
62 #define OS_WIN2008 (OS_FS+5)
63 #define OS_WIN7 (OS_FS+6)
64
65 static const DCE2_SmbFsm dce2_smb_service_fsm[] =
66 {
67 // IPC
68 { 'I', SERVICE_0+1, SERVICE_1 },
69 { 'P', SERVICE_0+2, SERVICE_FS },
70 { 'C', SERVICE_0+3, SERVICE_FS },
71 { '\0', SERVICE_IPC, SERVICE_FS },
72
73 // DISK
74 { 'A', SERVICE_1+1, SERVICE_FS },
75 { ':', SERVICE_1+2, SERVICE_FS },
76 { '\0', SERVICE_DISK, SERVICE_FS },
77
78 { 0, SERVICE_FS, SERVICE_FS }
79 };
80
81 static const DCE2_SmbFsm dce2_ipc_share_fsm[] =
82 {
83 { 'I', SHARE_0+1, SHARE_FS },
84 { 'P', SHARE_0+2, SHARE_FS },
85 { 'C', SHARE_0+3, SHARE_FS },
86 { '$', SHARE_0+4, SHARE_FS },
87 { '\0', SHARE_IPC, SHARE_FS },
88
89 { 0, SHARE_FS, SHARE_FS }
90 };
91
92 static const DCE2_SmbFsm dce2_smb_os_fsm[] =
93 {
94 // Windows start states
95 { 'W', OS_0+1, OS_FS },
96 { 'i', OS_0+2, OS_FS },
97 { 'n', OS_0+3, OS_FS },
98 { 'd', OS_0+4, OS_FS },
99 { 'o', OS_0+5, OS_FS },
100 { 'w', OS_0+6, OS_FS },
101 { 's', OS_0+7, OS_FS },
102 { ' ', OS_0+8, OS_FS },
103
104 // Windows 2000 and XP server states
105 { '5', OS_1+1, OS_2 },
106 { '.', OS_1+2, OS_FS },
107 { '1', OS_WINXP, OS_1+3 }, // Windows XP
108 { '0', OS_WIN2000, OS_FS }, // Windows 2000
109
110 // Windows 2000 or XP client states
111 { '2', OS_2+1, OS_3 },
112 { '0', OS_2+2, OS_FS },
113 { '0', OS_2+3, OS_FS },
114 { '2', OS_WINXP, OS_2+4 }, // Windows XP
115 { '0', OS_WIN2000, OS_FS }, // Windows 2000
116
117 // "Server" string states
118 { 'S', OS_3+ 1, OS_4 },
119 { 'e', OS_3+ 2, OS_FS },
120 { 'r', OS_3+ 3, OS_FS },
121 { 'v', OS_3+ 4, OS_FS },
122 { 'e', OS_3+ 5, OS_FS },
123 { 'r', OS_3+ 6, OS_FS },
124 { ' ', OS_3+ 7, OS_FS },
125 { '2', OS_3+ 8, OS_3+12 },
126 { '0', OS_3+ 9, OS_FS },
127 { '0', OS_3+10, OS_FS },
128 { '3', OS_WIN2003, OS_3+11 }, // Windows Server 2003
129 { '8', OS_WIN2008, OS_FS }, // Windows Server 2008R2
130
131 // Windows 2008 has this, 2008 R2 does not
132 { '(', OS_3+13, OS_FS },
133 { 'R', OS_3+14, OS_FS },
134 { ')', OS_3+15, OS_FS },
135 { ' ', OS_3+16, OS_FS },
136 { '2', OS_3+17, OS_FS },
137 { '0', OS_3+18, OS_FS },
138 { '0', OS_3+19, OS_FS },
139 { '8', OS_WIN2008, OS_FS },
140
141 // Windows Vista states
142 { 'V', OS_4+1, OS_5 },
143 { 'i', OS_4+2, OS_FS },
144 { 's', OS_4+3, OS_FS },
145 { 't', OS_4+4, OS_FS },
146 { 'a', OS_WINVISTA, OS_FS },
147
148 // Windows 7 state
149 { '7', OS_WIN7, OS_6 },
150
151 // Windows NT
152 { 'N', OS_6+1, OS_7 },
153 { 'T', OS_WIN2000, OS_FS }, // Windows NT, set policy to Windows 2000
154
155 // Windows 98
156 { '4', OS_7+1, OS_FS },
157 { '.', OS_7+2, OS_FS },
158 { '0', OS_WIN2000, OS_FS }, // Windows 98, set policy to Windows 2000
159
160 // Failure state
161 { 0, OS_FS, OS_FS }
162
163 // Match states shouldn't be accessed
164 };
165
166 /********************************************************************
167 * Private function prototypes
168 *******************************************************************/
169 static inline void DCE2_SmbCheckFmtData(DCE2_SmbSsnData*, const uint32_t,
170 const uint16_t, const uint8_t, const uint16_t, const uint16_t);
171 static DCE2_Ret DCE2_SmbCheckData(DCE2_SmbSsnData*, const uint8_t*,
172 const uint8_t*, const uint32_t, const uint16_t, const uint32_t, uint16_t);
173 static DCE2_Ret DCE2_SmbWriteAndXRawRequest(DCE2_SmbSsnData*, const SmbNtHdr*,
174 const DCE2_SmbComInfo*, const uint8_t*, uint32_t);
175
176 /*********************************************************************
177 * Private functions
178 ********************************************************************/
179
180 /********************************************************************
181 * Function: DCE2_SmbCheckFmtData()
182 *
183 * Purpose:
184 * Checks the data count in commands with formats, e.g.
185 * SMB_COM_WRITE, SMB_COM_WRITE_AND_CLOSE, SMB_COM_WRITE_AND_UNLOCK.
186 *
187 * Arguments:
188 * DCE2_SmbSsnData * - SMB session data structure
189 * const uint32_t - remaining NetBIOS PDU length
190 * const uint16_t - advertised byte count
191 * const uint8_t - data format specifier
192 * const uint16_t - data count reported in command
193 * const uint16_t - data count reported in format field
194 *
195 * Returns: None
196 *
197 ********************************************************************/
DCE2_SmbCheckFmtData(DCE2_SmbSsnData * ssd,const uint32_t nb_len,const uint16_t bcc,const uint8_t fmt,const uint16_t com_dcnt,const uint16_t fmt_dcnt)198 static inline void DCE2_SmbCheckFmtData(DCE2_SmbSsnData* ssd,
199 const uint32_t nb_len, const uint16_t bcc, const uint8_t fmt,
200 const uint16_t com_dcnt, const uint16_t fmt_dcnt)
201 {
202 if (fmt != SMB_FMT__DATA_BLOCK)
203 dce_alert(GID_DCE2, DCE2_SMB_BAD_FORM, (dce2CommonStats*)&dce2_smb_stats,
204 ssd->sd);
205 if (com_dcnt != fmt_dcnt)
206 dce_alert(GID_DCE2, DCE2_SMB_DCNT_MISMATCH, (dce2CommonStats*)&dce2_smb_stats,
207 ssd->sd);
208 if (com_dcnt != (bcc - 3))
209 dce_alert(GID_DCE2, DCE2_SMB_INVALID_DSIZE, (dce2CommonStats*)&dce2_smb_stats,
210 ssd->sd);
211 if (nb_len < com_dcnt)
212 dce_alert(GID_DCE2, DCE2_SMB_NB_LT_DSIZE, (dce2CommonStats*)&dce2_smb_stats,
213 ssd->sd);
214 }
215
216 /********************************************************************
217 * Function: DCE2_SmbCheckData()
218 *
219 * Purpose:
220 * Ensures that the data size reported in an SMB command is kosher.
221 *
222 * Arguments:
223 * DCE2_SmbSsnData * - SMB session data structure
224 * const uint8_t * - pointer to start of SMB header where offset is
225 * taken from.
226 * const uint8_t * - current pointer - should be right after command
227 * structure.
228 * const uint32_t - remaining data left in PDU from current pointer.
229 * const uint16_t - the byte count from the SMB command
230 * const uint16_t - reported data count in SMB command
231 * const uint16_t - reported data offset in SMB command
232 *
233 * Returns:
234 * DCE2_Ret - DCE2_RET__ERROR if data should not be processed
235 * DCE2_RET__SUCCESS if data can be processed
236 *
237 ********************************************************************/
DCE2_SmbCheckData(DCE2_SmbSsnData * ssd,const uint8_t * smb_hdr_ptr,const uint8_t * nb_ptr,const uint32_t nb_len,const uint16_t bcc,const uint32_t dcnt,uint16_t doff)238 static DCE2_Ret DCE2_SmbCheckData(DCE2_SmbSsnData* ssd,
239 const uint8_t* smb_hdr_ptr, const uint8_t* nb_ptr,
240 const uint32_t nb_len, const uint16_t bcc,
241 const uint32_t dcnt, uint16_t doff)
242 {
243 const uint8_t* offset = smb_hdr_ptr + doff;
244 const uint8_t* nb_end = nb_ptr + nb_len;
245
246 // Byte counts don't usually matter, so no error but still alert
247 // Don't alert in the case where the data count is larger than what the
248 // byte count can handle. This can happen if CAP_LARGE_READX or
249 // CAP_LARGE_WRITEX were negotiated.
250 if ((dcnt <= UINT16_MAX) && (bcc < dcnt))
251 dce_alert(GID_DCE2, DCE2_SMB_BCC_LT_DSIZE, (dce2CommonStats*)&dce2_smb_stats,
252 ssd->sd);
253
254 if (offset > nb_end)
255 {
256 dce_alert(GID_DCE2, DCE2_SMB_BAD_OFF, (dce2CommonStats*)&dce2_smb_stats, ssd->sd);
257 // Error if offset is beyond data left
258 return DCE2_RET__ERROR;
259 }
260
261 // Only check if the data count is non-zero
262 if ((dcnt != 0) && (offset < nb_ptr))
263 {
264 // Not necessarily and error if the offset puts the data
265 // before or in the command structure.
266 dce_alert(GID_DCE2, DCE2_SMB_BAD_OFF, (dce2CommonStats*)&dce2_smb_stats, ssd->sd);
267 }
268
269 // Not necessarily an error if the addition of the data count goes
270 // beyond the data left
271
272 if (dcnt > (nb_end - offset)) // beyond data left
273 {
274 dce_alert(GID_DCE2, DCE2_SMB_NB_LT_DSIZE, (dce2CommonStats*)&dce2_smb_stats,
275 ssd->sd);
276 }
277
278 return DCE2_RET__SUCCESS;
279 }
280
281 // SMB_COM_WRITE_ANDX - raw mode
DCE2_SmbWriteAndXRawRequest(DCE2_SmbSsnData * ssd,const SmbNtHdr * smb_hdr,const DCE2_SmbComInfo * com_info,const uint8_t * nb_ptr,uint32_t nb_len)282 static DCE2_Ret DCE2_SmbWriteAndXRawRequest(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
283 const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
284 {
285 // Set this now for possible reassembled packet
286 uint16_t fid = SmbWriteAndXReqFid((const SmbWriteAndXReq*)nb_ptr);
287 DCE2_SmbFileTracker* ftracker = DCE2_SmbGetFileTracker(ssd, fid);
288 ssd->cur_rtracker->ftracker = ftracker;
289 if (ftracker == nullptr)
290 return DCE2_RET__ERROR;
291
292 // Got request to write in raw mode without having gotten the initial
293 // raw mode request or got initial raw mode request and then another
294 // without having finished the first.
295 bool start_write_raw = SmbWriteAndXReqStartRaw((const SmbWriteAndXReq*)nb_ptr);
296 bool continue_write_raw = SmbWriteAndXReqRaw((const SmbWriteAndXReq*)nb_ptr);
297 DCE2_Policy policy = DCE2_SsnGetServerPolicy(&ssd->sd);
298 if ((start_write_raw && (ftracker->fp_writex_raw != nullptr)
299 && (ftracker->fp_writex_raw->remaining != 0))
300 || (continue_write_raw && ((ftracker->fp_writex_raw == nullptr)
301 || (ftracker->fp_writex_raw->remaining == 0))))
302 {
303 switch (policy)
304 {
305 case DCE2_POLICY__WIN2000:
306 case DCE2_POLICY__WINXP:
307 case DCE2_POLICY__WINVISTA:
308 case DCE2_POLICY__WIN2003:
309 case DCE2_POLICY__WIN2008:
310 case DCE2_POLICY__WIN7:
311 if (ftracker->fp_writex_raw != nullptr)
312 {
313 ftracker->fp_writex_raw->remaining = 0;
314 DCE2_BufferEmpty(ftracker->fp_writex_raw->buf);
315 }
316 return DCE2_RET__ERROR;
317 case DCE2_POLICY__SAMBA:
318 case DCE2_POLICY__SAMBA_3_0_37:
319 case DCE2_POLICY__SAMBA_3_0_22:
320 case DCE2_POLICY__SAMBA_3_0_20:
321 // Samba doesn't do anything special here except if the two
322 // flags are set it walks past the two "length" bytes.
323 // See below.
324 break;
325 default:
326 assert(false);
327 break;
328 }
329 }
330
331 uint16_t com_size = DCE2_ComInfoCommandSize(com_info);
332 uint16_t byte_count = DCE2_ComInfoByteCount(com_info);
333 uint16_t doff = SmbWriteAndXReqDataOff((const SmbWriteAndXReq*)nb_ptr);
334 uint32_t dcnt = SmbWriteAndXReqDataCnt((const SmbWriteAndXReq*)nb_ptr);
335 uint16_t remaining = SmbWriteAndXReqRemaining((const SmbWriteAndXReq*)nb_ptr);
336
337 dce2_move(nb_ptr, nb_len, com_size);
338
339 if (DCE2_SmbCheckData(ssd, (const uint8_t*)smb_hdr, nb_ptr, nb_len,
340 byte_count, dcnt, doff) != DCE2_RET__SUCCESS)
341 return DCE2_RET__ERROR;
342
343 // This may move backwards
344 dce2_move(nb_ptr, nb_len, ((const uint8_t*)smb_hdr + doff) - nb_ptr);
345
346 // If a "raw" write is requested there will be two bytes after the
347 // header/pad and before the data which is supposed to represent a
348 // length but everyone ignores it. However we need to move past it.
349 // This is the one situation where the remaining field matters and
350 // should be equal to the total amount of data to be written.
351 if (start_write_raw)
352 {
353 if (dcnt < 2)
354 return DCE2_RET__ERROR;
355
356 // From data size check above, nb_len >= dsize
357 dcnt -= 2;
358 dce2_move(nb_ptr, nb_len, 2);
359 }
360
361 if (dcnt > nb_len)
362 dcnt = nb_len;
363
364 // File tracker already validated
365 switch (policy)
366 {
367 case DCE2_POLICY__WIN2000:
368 case DCE2_POLICY__WINXP:
369 case DCE2_POLICY__WINVISTA:
370 case DCE2_POLICY__WIN2003:
371 case DCE2_POLICY__WIN2008:
372 case DCE2_POLICY__WIN7:
373 if (start_write_raw)
374 {
375 if (ftracker->fp_writex_raw == nullptr)
376 {
377 ftracker->fp_writex_raw = (DCE2_SmbWriteAndXRaw*)
378 snort_calloc(sizeof(DCE2_SmbWriteAndXRaw));
379 if (ftracker->fp_writex_raw == nullptr)
380 return DCE2_RET__ERROR;
381
382 ftracker->fp_writex_raw->remaining = (int)remaining;
383 }
384 }
385
386 ftracker->fp_writex_raw->remaining -= (int)dcnt;
387 if (ftracker->fp_writex_raw->remaining < 0)
388 {
389 ftracker->fp_writex_raw->remaining = 0;
390 DCE2_BufferEmpty(ftracker->fp_writex_raw->buf);
391 return DCE2_RET__ERROR;
392 }
393
394 // If the "raw" write isn't finished in the first request
395 // and haven't allocated a buffer yet.
396 if (start_write_raw && (ftracker->fp_writex_raw->remaining != 0)
397 && (ftracker->fp_writex_raw->buf == nullptr))
398 {
399 ftracker->fp_writex_raw->buf =
400 DCE2_BufferNew(remaining, 0);
401 if (ftracker->fp_writex_raw->buf == nullptr)
402 {
403 ftracker->fp_writex_raw->remaining = 0;
404 return DCE2_RET__ERROR;
405 }
406 }
407
408 // If data has to be added to buffer, i.e. not a start raw
409 // or a start raw and more raw requests to come.
410 if (!start_write_raw || (ftracker->fp_writex_raw->remaining != 0))
411 {
412 if (DCE2_BufferAddData(ftracker->fp_writex_raw->buf, nb_ptr,
413 dcnt, DCE2_BufferLength(ftracker->fp_writex_raw->buf),
414 DCE2_BUFFER_MIN_ADD_FLAG__IGNORE) != DCE2_RET__SUCCESS)
415 {
416 ftracker->fp_writex_raw->remaining = 0;
417 DCE2_BufferEmpty(ftracker->fp_writex_raw->buf);
418 return DCE2_RET__ERROR;
419 }
420
421 if (ftracker->fp_writex_raw->remaining == 0)
422 {
423 const uint8_t* data_ptr = DCE2_BufferData(ftracker->fp_writex_raw->buf);
424 uint32_t data_len = DCE2_BufferLength(ftracker->fp_writex_raw->buf);
425 Packet* rpkt = DCE2_SmbGetRpkt(ssd,
426 &data_ptr, &data_len, DCE2_RPKT_TYPE__SMB_TRANS);
427
428 if (rpkt == nullptr)
429 {
430 DCE2_BufferEmpty(ftracker->fp_writex_raw->buf);
431 return DCE2_RET__ERROR;
432 }
433
434 (void)DCE2_SmbProcessRequestData(ssd, fid, data_ptr, data_len, 0);
435
436 DCE2_BufferEmpty(ftracker->fp_writex_raw->buf);
437 }
438 }
439 else
440 {
441 (void)DCE2_SmbProcessRequestData(ssd, fid, nb_ptr, dcnt, 0);
442 }
443
444 // Windows doesn't process chained commands to raw WriteAndXs
445 // so return error so it exits the loop.
446 return DCE2_RET__ERROR;
447
448 case DCE2_POLICY__SAMBA:
449 case DCE2_POLICY__SAMBA_3_0_37:
450 case DCE2_POLICY__SAMBA_3_0_22:
451 case DCE2_POLICY__SAMBA_3_0_20:
452 // All Samba cares about is skipping the 2 byte "length"
453 // if both flags are set.
454 break;
455 default:
456 assert(false);
457 break;
458 }
459
460 return DCE2_SmbProcessRequestData(ssd, fid, nb_ptr, dcnt, 0);
461 }
462
DCE2_SmbSetFingerprintedClient(DCE2_SmbSsnData * ssd)463 static inline void DCE2_SmbSetFingerprintedClient(DCE2_SmbSsnData* ssd)
464 {
465 ssd->ssn_state_flags |= DCE2_SMB_SSN_STATE__FP_CLIENT;
466 }
467
DCE2_SmbFingerprintedClient(DCE2_SmbSsnData * ssd)468 static inline bool DCE2_SmbFingerprintedClient(DCE2_SmbSsnData* ssd)
469 {
470 return ssd->ssn_state_flags & DCE2_SMB_SSN_STATE__FP_CLIENT;
471 }
472
DCE2_SmbSetFingerprintedServer(DCE2_SmbSsnData * ssd)473 static inline void DCE2_SmbSetFingerprintedServer(DCE2_SmbSsnData* ssd)
474 {
475 ssd->ssn_state_flags |= DCE2_SMB_SSN_STATE__FP_SERVER;
476 }
477
DCE2_SmbFingerprintedServer(DCE2_SmbSsnData * ssd)478 static inline bool DCE2_SmbFingerprintedServer(DCE2_SmbSsnData* ssd)
479 {
480 return ssd->ssn_state_flags & DCE2_SMB_SSN_STATE__FP_SERVER;
481 }
482
483 /********************************************************************
484 * Functions:
485 * DCE2_SmbOpen()
486 * DCE2_SmbCreate()
487 * DCE2_SmbClose()
488 * DCE2_SmbRename()
489 * DCE2_SmbRead()
490 * DCE2_SmbWrite()
491 * DCE2_SmbCreateNew()
492 * DCE2_SmbLockAndRead()
493 * DCE2_SmbWriteAndUnlock()
494 * DCE2_SmbReadRaw()
495 * DCE2_SmbWriteRaw()
496 * DCE2_SmbWriteComplete()
497 * DCE2_SmbWriteAndClose()
498 * DCE2_SmbOpenAndX()
499 * DCE2_SmbReadAndX()
500 * DCE2_SmbWriteAndX()
501 * DCE2_SmbTreeConnect()
502 * DCE2_SmbTreeDisconnect()
503 * DCE2_SmbNegotiate()
504 * DCE2_SmbSessionSetupAndX()
505 * DCE2_SmbLogoffAndX()
506 * DCE2_SmbTreeConnectAndX()
507 * DCE2_SmbNtCreateAndX()
508 *
509 * Purpose: Process SMB command
510 *
511 * Arguments:
512 * DCE2_SmbSsnData * - SMB session data structure
513 * const SmbNtHdr * - SMB header structure (packet pointer)
514 * const DCE2_SmbComInfo * - Basic command information structure
515 * uint8_t * - pointer to start of command (packet pointer)
516 * uint32_t - remaining NetBIOS length
517 *
518 * Returns:
519 * DCE2_Ret - DCE2_RET__ERROR if something went wrong and/or processing
520 * should stop
521 * DCE2_RET__SUCCESS if processing should continue
522 *
523 ********************************************************************/
524
525 // SMB_COM_OPEN
DCE2_SmbOpen(DCE2_SmbSsnData * ssd,const SmbNtHdr * smb_hdr,const DCE2_SmbComInfo * com_info,const uint8_t * nb_ptr,uint32_t nb_len)526 DCE2_Ret DCE2_SmbOpen(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
527 const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
528 {
529 if (!DCE2_ComInfoCanProcessCommand(com_info))
530 return DCE2_RET__ERROR;
531
532 if (DCE2_ComInfoIsResponse(com_info))
533 {
534 DCE2_SmbFileTracker* ftracker;
535
536 if (!DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid)
537 && (SmbFileAttrsDirectory(SmbOpenRespFileAttrs((const SmbOpenResp*)nb_ptr))
538 || SmbOpenForWriting(SmbOpenRespAccessMode((const SmbOpenResp*)nb_ptr))))
539 return DCE2_RET__SUCCESS;
540
541 ftracker = DCE2_SmbNewFileTracker(ssd, ssd->cur_rtracker->uid,
542 ssd->cur_rtracker->tid, SmbOpenRespFid((const SmbOpenResp*)nb_ptr));
543 if (ftracker == nullptr)
544 return DCE2_RET__ERROR;
545
546 DCE2_Update_Ftracker_from_ReqTracker(ftracker, ssd->cur_rtracker);
547
548 if (!ftracker->is_ipc)
549 {
550 // This command can only be used to open an existing file
551 ftracker->ff_file_size = SmbOpenRespFileSize((const SmbOpenResp*)nb_ptr);
552 }
553 }
554 else
555 {
556 // Have at least 2 bytes of data based on byte count check done earlier
557
558 dce2_move(nb_ptr, nb_len, DCE2_ComInfoCommandSize(com_info));
559
560 if (!SmbFmtAscii(*nb_ptr))
561 {
562 dce_alert(GID_DCE2, DCE2_SMB_BAD_FORM, (dce2CommonStats*)&dce2_smb_stats,
563 ssd->sd);
564 return DCE2_RET__ERROR;
565 }
566
567 dce2_move(nb_ptr, nb_len, 1);
568
569 ssd->cur_rtracker->file_name =
570 get_smb_file_name(nb_ptr, nb_len, SmbUnicode(smb_hdr),
571 &ssd->cur_rtracker->file_name_size);
572 }
573
574 return DCE2_RET__SUCCESS;
575 }
576
577 // SMB_COM_CREATE
DCE2_SmbCreate(DCE2_SmbSsnData * ssd,const SmbNtHdr * smb_hdr,const DCE2_SmbComInfo * com_info,const uint8_t * nb_ptr,uint32_t nb_len)578 DCE2_Ret DCE2_SmbCreate(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
579 const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
580 {
581 if (!DCE2_ComInfoCanProcessCommand(com_info))
582 return DCE2_RET__ERROR;
583
584 if (DCE2_ComInfoIsResponse(com_info))
585 {
586 DCE2_SmbFileTracker* ftracker = DCE2_SmbNewFileTracker(
587 ssd, ssd->cur_rtracker->uid, ssd->cur_rtracker->tid,
588 SmbCreateRespFid((const SmbCreateResp*)nb_ptr));
589
590 if (ftracker == nullptr)
591 return DCE2_RET__ERROR;
592
593 DCE2_Update_Ftracker_from_ReqTracker(ftracker, ssd->cur_rtracker);
594
595 // Command creates or opens and truncates file to 0 so assume
596 // upload.
597 if (!ftracker->is_ipc)
598 ftracker->ff_file_direction = DCE2_SMB_FILE_DIRECTION__UPLOAD;
599 }
600 else
601 {
602 if (!DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid))
603 {
604 uint16_t file_attrs = SmbCreateReqFileAttrs((const SmbCreateReq*)nb_ptr);
605
606 if (SmbAttrDirectory(file_attrs))
607 return DCE2_RET__IGNORE;
608
609 if (SmbEvasiveFileAttrs(file_attrs))
610 dce_alert(GID_DCE2, DCE2_SMB_EVASIVE_FILE_ATTRS,
611 (dce2CommonStats*)&dce2_smb_stats, ssd->sd);
612 }
613
614 // Have at least 2 bytes of data based on byte count check done earlier
615
616 dce2_move(nb_ptr, nb_len, DCE2_ComInfoCommandSize(com_info));
617 assert(nb_ptr != nullptr);
618 if (!SmbFmtAscii(*nb_ptr))
619 {
620 dce_alert(GID_DCE2, DCE2_SMB_BAD_FORM, (dce2CommonStats*)&dce2_smb_stats,
621 ssd->sd);
622 return DCE2_RET__ERROR;
623 }
624
625 dce2_move(nb_ptr, nb_len, 1);
626
627 ssd->cur_rtracker->file_name =
628 get_smb_file_name(nb_ptr, nb_len, SmbUnicode(smb_hdr),
629 &ssd->cur_rtracker->file_name_size);
630 }
631
632 return DCE2_RET__SUCCESS;
633 }
634
635 // SMB_COM_CLOSE
DCE2_SmbClose(DCE2_SmbSsnData * ssd,const SmbNtHdr *,const DCE2_SmbComInfo * com_info,const uint8_t * nb_ptr,uint32_t)636 DCE2_Ret DCE2_SmbClose(DCE2_SmbSsnData* ssd, const SmbNtHdr*,
637 const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t)
638 {
639 if (!DCE2_ComInfoCanProcessCommand(com_info))
640 return DCE2_RET__ERROR;
641
642 if (DCE2_ComInfoIsRequest(com_info))
643 {
644 uint16_t fid = SmbCloseReqFid((const SmbCloseReq*)nb_ptr);
645
646 // Set this for response
647 ssd->cur_rtracker->ftracker = DCE2_SmbGetFileTracker(ssd, fid);
648
649 if ((ssd->fb_ftracker != nullptr) && (ssd->fb_ftracker == ssd->cur_rtracker->ftracker))
650 {
651 FileVerdict verdict = DCE2_get_file_verdict();
652
653 if ((verdict == FILE_VERDICT_BLOCK) || (verdict == FILE_VERDICT_REJECT))
654 ssd->block_pdus = true;
655 }
656 }
657 else
658 {
659 DCE2_SmbRemoveFileTracker(ssd, ssd->cur_rtracker->ftracker);
660 }
661
662 return DCE2_RET__SUCCESS;
663 }
664
665 // SMB_COM_RENAME
DCE2_SmbRename(DCE2_SmbSsnData * ssd,const SmbNtHdr * smb_hdr,const DCE2_SmbComInfo * com_info,const uint8_t * nb_ptr,uint32_t nb_len)666 DCE2_Ret DCE2_SmbRename(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
667 const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
668 {
669 // NOTE: This command is only processed for CVE-2006-4696 where the buffer
670 // formats are invalid and has no bearing on DCE/RPC processing.
671
672 if (!DCE2_ComInfoCanProcessCommand(com_info))
673 return DCE2_RET__ERROR;
674
675 if (DCE2_ComInfoIsRequest(com_info))
676 {
677 // Have at least 4 bytes of data based on byte count check done earlier
678
679 uint32_t i;
680
681 dce2_move(nb_ptr, nb_len, DCE2_ComInfoCommandSize(com_info));
682
683 if (!SmbFmtAscii(*nb_ptr))
684 {
685 dce_alert(GID_DCE2, DCE2_SMB_BAD_FORM, (dce2CommonStats*)&dce2_smb_stats,
686 ssd->sd);
687 return DCE2_RET__ERROR;
688 }
689
690 dce2_move(nb_ptr, nb_len, 1);
691
692 if (SmbUnicode(smb_hdr))
693 {
694 for (i = 0; i < (nb_len - 1); i += 2)
695 {
696 if (*((const uint16_t*)(nb_ptr + i)) == 0)
697 {
698 i += 2; // move past null terminating bytes
699 break;
700 }
701 }
702 }
703 else
704 {
705 for (i = 0; i < nb_len; i++)
706 {
707 if (nb_ptr[i] == 0)
708 {
709 i++; // move past null terminating byte
710 break;
711 }
712 }
713 }
714
715 // i <= nb_len
716 dce2_move(nb_ptr, nb_len, i);
717
718 if ((nb_len > 0) && !SmbFmtAscii(*nb_ptr))
719 {
720 dce_alert(GID_DCE2, DCE2_SMB_BAD_FORM, (dce2CommonStats*)&dce2_smb_stats,
721 ssd->sd);
722 return DCE2_RET__ERROR;
723 }
724 }
725
726 // Don't care about tracking response
727 return DCE2_RET__ERROR;
728 }
729
730 // SMB_COM_READ
DCE2_SmbRead(DCE2_SmbSsnData * ssd,const SmbNtHdr *,const DCE2_SmbComInfo * com_info,const uint8_t * nb_ptr,uint32_t nb_len)731 DCE2_Ret DCE2_SmbRead(DCE2_SmbSsnData* ssd, const SmbNtHdr*,
732 const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
733 {
734 if (!DCE2_ComInfoCanProcessCommand(com_info))
735 return DCE2_RET__ERROR;
736
737 if (DCE2_ComInfoIsRequest(com_info))
738 {
739 DCE2_SmbFileTracker* ftracker =
740 DCE2_SmbGetFileTracker(ssd, SmbReadReqFid((const SmbReadReq*)nb_ptr));
741
742 // Set this for response since response doesn't have the Fid
743 ssd->cur_rtracker->ftracker = ftracker;
744 if ((ftracker != nullptr) && !ftracker->is_ipc)
745 ssd->cur_rtracker->file_offset = SmbReadReqOffset((const SmbReadReq*)nb_ptr);
746 }
747 else
748 {
749 // Have at least 3 bytes of data based on byte count check done earlier
750
751 uint16_t com_size = DCE2_ComInfoCommandSize(com_info);
752 uint16_t byte_count = DCE2_ComInfoByteCount(com_info);
753 uint16_t com_dcnt = SmbReadRespCount((const SmbReadResp*)nb_ptr);
754 uint8_t fmt = *(nb_ptr + com_size);
755 uint16_t fmt_dcnt = alignedNtohs((const uint16_t*)(nb_ptr + com_size + 1));
756
757 dce2_move(nb_ptr, nb_len, (com_size + 3));
758
759 DCE2_SmbCheckFmtData(ssd, nb_len, byte_count, fmt, com_dcnt, fmt_dcnt);
760
761 if (com_dcnt > nb_len)
762 return DCE2_RET__ERROR;
763
764 return DCE2_SmbProcessResponseData(ssd, nb_ptr, com_dcnt);
765 }
766
767 return DCE2_RET__SUCCESS;
768 }
769
770 // SMB_COM_WRITE
DCE2_SmbWrite(DCE2_SmbSsnData * ssd,const SmbNtHdr *,const DCE2_SmbComInfo * com_info,const uint8_t * nb_ptr,uint32_t nb_len)771 DCE2_Ret DCE2_SmbWrite(DCE2_SmbSsnData* ssd, const SmbNtHdr*,
772 const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
773 {
774 if (!DCE2_ComInfoCanProcessCommand(com_info))
775 return DCE2_RET__ERROR;
776
777 if (DCE2_ComInfoIsRequest(com_info))
778 {
779 // Have at least 3 bytes of data based on byte count check done earlier
780
781 uint16_t com_size = DCE2_ComInfoCommandSize(com_info);
782 uint16_t byte_count = DCE2_ComInfoByteCount(com_info);
783 uint8_t fmt = *(nb_ptr + com_size);
784 uint16_t com_dcnt = SmbWriteReqCount((const SmbWriteReq*)nb_ptr);
785 uint16_t fmt_dcnt = alignedNtohs((const uint16_t*)(nb_ptr + com_size + 1));
786 uint16_t fid = SmbWriteReqFid((const SmbWriteReq*)nb_ptr);
787 uint32_t offset = SmbWriteReqOffset((const SmbWriteReq*)nb_ptr);
788
789 dce2_move(nb_ptr, nb_len, (com_size + 3));
790
791 DCE2_SmbCheckFmtData(ssd, nb_len, byte_count, fmt, com_dcnt, fmt_dcnt);
792
793 if (com_dcnt == 0)
794 {
795 dce_alert(GID_DCE2, DCE2_SMB_DCNT_ZERO, (dce2CommonStats*)&dce2_smb_stats,
796 ssd->sd);
797 return DCE2_RET__ERROR;
798 }
799
800 if (com_dcnt > nb_len)
801 com_dcnt = (uint16_t)nb_len;
802
803 return DCE2_SmbProcessRequestData(ssd, fid, nb_ptr, com_dcnt, offset);
804 }
805
806 return DCE2_RET__SUCCESS;
807 }
808
809 // SMB_COM_CREATE_NEW
DCE2_SmbCreateNew(DCE2_SmbSsnData * ssd,const SmbNtHdr * smb_hdr,const DCE2_SmbComInfo * com_info,const uint8_t * nb_ptr,uint32_t nb_len)810 DCE2_Ret DCE2_SmbCreateNew(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
811 const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
812 {
813 if (!DCE2_ComInfoCanProcessCommand(com_info))
814 return DCE2_RET__ERROR;
815
816 if (DCE2_ComInfoIsResponse(com_info))
817 {
818 DCE2_SmbFileTracker* ftracker = DCE2_SmbNewFileTracker(
819 ssd, ssd->cur_rtracker->uid, ssd->cur_rtracker->tid,
820 SmbCreateNewRespFid((const SmbCreateNewResp*)nb_ptr));
821
822 if (ftracker == nullptr)
823 return DCE2_RET__ERROR;
824
825 DCE2_Update_Ftracker_from_ReqTracker(ftracker, ssd->cur_rtracker);
826
827 // Command creates a new file so assume upload.
828 if (!ftracker->is_ipc)
829 ftracker->ff_file_direction = DCE2_SMB_FILE_DIRECTION__UPLOAD;
830 }
831 else
832 {
833 if (!DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid))
834 {
835 uint16_t file_attrs = SmbCreateNewReqFileAttrs((const SmbCreateNewReq*)nb_ptr);
836
837 if (SmbAttrDirectory(file_attrs))
838 return DCE2_RET__IGNORE;
839
840 if (SmbEvasiveFileAttrs(file_attrs))
841 dce_alert(GID_DCE2, DCE2_SMB_EVASIVE_FILE_ATTRS,
842 (dce2CommonStats*)&dce2_smb_stats, ssd->sd);
843 }
844
845 // Have at least 2 bytes of data based on byte count check done earlier
846
847 dce2_move(nb_ptr, nb_len, DCE2_ComInfoCommandSize(com_info));
848 assert(nb_ptr != nullptr);
849 if (!SmbFmtAscii(*nb_ptr))
850 {
851 dce_alert(GID_DCE2, DCE2_SMB_BAD_FORM, (dce2CommonStats*)&dce2_smb_stats,
852 ssd->sd);
853 return DCE2_RET__ERROR;
854 }
855
856 dce2_move(nb_ptr, nb_len, 1);
857
858 ssd->cur_rtracker->file_name =
859 get_smb_file_name(nb_ptr, nb_len, SmbUnicode(smb_hdr),
860 &ssd->cur_rtracker->file_name_size);
861 }
862
863 return DCE2_RET__SUCCESS;
864 }
865
866 // SMB_COM_LOCK_AND_READ
DCE2_SmbLockAndRead(DCE2_SmbSsnData * ssd,const SmbNtHdr *,const DCE2_SmbComInfo * com_info,const uint8_t * nb_ptr,uint32_t nb_len)867 DCE2_Ret DCE2_SmbLockAndRead(DCE2_SmbSsnData* ssd, const SmbNtHdr*,
868 const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
869 {
870 if (!DCE2_ComInfoCanProcessCommand(com_info))
871 return DCE2_RET__ERROR;
872
873 if (DCE2_ComInfoIsRequest(com_info))
874 {
875 DCE2_SmbFileTracker* ftracker =
876 DCE2_SmbFindFileTracker(ssd, ssd->cur_rtracker->uid,
877 ssd->cur_rtracker->tid, SmbLockAndReadReqFid((const SmbLockAndReadReq*)nb_ptr));
878
879 // No sense in tracking response
880 if (ftracker == nullptr)
881 return DCE2_RET__ERROR;
882
883 if (!ftracker->is_ipc)
884 ssd->cur_rtracker->file_offset = SmbLockAndReadReqOffset((const
885 SmbLockAndReadReq*)nb_ptr);
886
887 // Set this for response
888 ssd->cur_rtracker->ftracker = ftracker;
889 }
890 else
891 {
892 // Have at least 3 bytes of data based on byte count check done earlier
893 uint16_t com_size = DCE2_ComInfoCommandSize(com_info);
894 uint16_t byte_count = DCE2_ComInfoByteCount(com_info);
895 uint8_t fmt = *(nb_ptr + com_size);
896 uint16_t com_dcnt = SmbLockAndReadRespCount((const SmbLockAndReadResp*)nb_ptr);
897 uint16_t fmt_dcnt = alignedNtohs((const uint16_t*)(nb_ptr + com_size + 1));
898
899 dce2_move(nb_ptr, nb_len, (com_size + 3));
900
901 DCE2_SmbCheckFmtData(ssd, nb_len, byte_count, fmt, com_dcnt, fmt_dcnt);
902
903 if (com_dcnt == 0)
904 {
905 dce_alert(GID_DCE2, DCE2_SMB_DCNT_ZERO, (dce2CommonStats*)&dce2_smb_stats,
906 ssd->sd);
907 return DCE2_RET__ERROR;
908 }
909
910 if (com_dcnt > nb_len)
911 com_dcnt = (uint16_t)nb_len;
912
913 return DCE2_SmbProcessResponseData(ssd, nb_ptr, com_dcnt);
914 }
915
916 return DCE2_RET__SUCCESS;
917 }
918
919 // SMB_COM_WRITE_AND_UNLOCK
DCE2_SmbWriteAndUnlock(DCE2_SmbSsnData * ssd,const SmbNtHdr * smb_hdr,const DCE2_SmbComInfo * com_info,const uint8_t * nb_ptr,uint32_t nb_len)920 DCE2_Ret DCE2_SmbWriteAndUnlock(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
921 const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
922 {
923 if (!DCE2_ComInfoCanProcessCommand(com_info))
924 {
925 if (DCE2_ComInfoIsBadLength(com_info) || DCE2_ComInfoIsInvalidWordCount(com_info))
926 return DCE2_RET__ERROR;
927
928 // These are special cases. The write succeeds but the unlock fails
929 // so an error response is returned but the data was actually written.
930 if (DCE2_ComInfoIsResponse(com_info) && DCE2_ComInfoIsStatusError(com_info))
931 {
932 if (DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid))
933 {
934 if (!SmbErrorInvalidDeviceRequest(smb_hdr))
935 return DCE2_RET__ERROR;
936 }
937 else if (!SmbErrorRangeNotLocked(smb_hdr))
938 {
939 return DCE2_RET__ERROR;
940 }
941 }
942 }
943
944 if (DCE2_ComInfoIsRequest(com_info))
945 {
946 // Have at least 3 bytes of data based on byte count check done earlier
947
948 uint16_t com_size = DCE2_ComInfoCommandSize(com_info);
949 uint16_t byte_count = DCE2_ComInfoByteCount(com_info);
950 uint8_t fmt = *(nb_ptr + com_size);
951 uint16_t com_dcnt = SmbWriteAndUnlockReqCount((const SmbWriteAndUnlockReq*)nb_ptr);
952 uint16_t fmt_dcnt = alignedNtohs((const uint16_t*)(nb_ptr + com_size + 1));
953 uint16_t fid = SmbWriteAndUnlockReqFid((const SmbWriteAndUnlockReq*)nb_ptr);
954 uint32_t offset = SmbWriteAndUnlockReqOffset((const SmbWriteAndUnlockReq*)nb_ptr);
955
956 dce2_move(nb_ptr, nb_len, (com_size + 3));
957
958 DCE2_SmbCheckFmtData(ssd, nb_len, byte_count, fmt, com_dcnt, fmt_dcnt);
959
960 if (com_dcnt == 0)
961 {
962 dce_alert(GID_DCE2, DCE2_SMB_DCNT_ZERO, (dce2CommonStats*)&dce2_smb_stats,
963 ssd->sd);
964 return DCE2_RET__ERROR;
965 }
966
967 if (com_dcnt > nb_len)
968 com_dcnt = (uint16_t)nb_len;
969
970 return DCE2_SmbProcessRequestData(ssd, fid, nb_ptr, com_dcnt, offset);
971 }
972
973 return DCE2_RET__SUCCESS;
974 }
975
976 // SMB_COM_OPEN_ANDX
DCE2_SmbOpenAndX(DCE2_SmbSsnData * ssd,const SmbNtHdr * smb_hdr,const DCE2_SmbComInfo * com_info,const uint8_t * nb_ptr,uint32_t nb_len)977 DCE2_Ret DCE2_SmbOpenAndX(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
978 const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
979 {
980 if (!DCE2_ComInfoCanProcessCommand(com_info))
981 return DCE2_RET__ERROR;
982
983 if (DCE2_ComInfoIsResponse(com_info))
984 {
985 const uint16_t fid = SmbOpenAndXRespFid((const SmbOpenAndXResp*)nb_ptr);
986 const uint16_t file_attrs = SmbOpenAndXRespFileAttrs((const SmbOpenAndXResp*)nb_ptr);
987 const uint16_t resource_type = SmbOpenAndXRespResourceType((const SmbOpenAndXResp*)nb_ptr);
988 DCE2_SmbFileTracker* ftracker = nullptr;
989
990 // Set request tracker's current file tracker in case of chained commands
991 switch (SmbAndXCom2((const SmbAndXCommon*)nb_ptr))
992 {
993 // This is in case in the request a write was chained to an open
994 // in which case the write will be to the newly opened file
995 case SMB_COM_WRITE:
996 case SMB_COM_WRITE_ANDX:
997 case SMB_COM_TRANSACTION:
998 case SMB_COM_READ_ANDX:
999 ftracker = DCE2_SmbDequeueTmpFileTracker(ssd, ssd->cur_rtracker, fid);
1000 break;
1001 default:
1002 break;
1003 }
1004
1005 if (!DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid)
1006 && (SmbFileAttrsDirectory(file_attrs)
1007 || !SmbResourceTypeDisk(resource_type)))
1008 {
1009 if (ftracker != nullptr)
1010 DCE2_SmbRemoveFileTracker(ssd, ftracker);
1011 return DCE2_RET__SUCCESS;
1012 }
1013
1014 if (ftracker == nullptr)
1015 {
1016 ftracker = DCE2_SmbNewFileTracker(ssd,
1017 ssd->cur_rtracker->uid, ssd->cur_rtracker->tid, fid);
1018 if (ftracker == nullptr)
1019 return DCE2_RET__ERROR;
1020 }
1021
1022 DCE2_Update_Ftracker_from_ReqTracker(ftracker, ssd->cur_rtracker);
1023
1024 if (!ftracker->is_ipc)
1025 {
1026 const uint16_t open_results = SmbOpenAndXRespOpenResults((const SmbOpenAndXResp*)nb_ptr);
1027
1028 if (SmbOpenResultRead(open_results))
1029 {
1030 ftracker->ff_file_size = SmbOpenAndXRespFileSize((const SmbOpenAndXResp*)nb_ptr);
1031 }
1032 else
1033 {
1034 ftracker->ff_file_size = ssd->cur_rtracker->file_size;
1035 ftracker->ff_file_direction = DCE2_SMB_FILE_DIRECTION__UPLOAD;
1036 }
1037 }
1038
1039 ssd->cur_rtracker->ftracker = ftracker;
1040 }
1041 else
1042 {
1043 uint32_t pad = 0;
1044 const bool unicode = SmbUnicode(smb_hdr);
1045 uint8_t null_bytes = unicode ? 2 : 1;
1046
1047 if (!DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid))
1048 {
1049 uint16_t file_attrs = SmbOpenAndXReqFileAttrs((const SmbOpenAndXReq*)nb_ptr);
1050
1051 if (SmbEvasiveFileAttrs(file_attrs))
1052 dce_alert(GID_DCE2, DCE2_SMB_EVASIVE_FILE_ATTRS,
1053 (dce2CommonStats*)&dce2_smb_stats, ssd->sd);
1054 ssd->cur_rtracker->file_size = SmbOpenAndXReqAllocSize((const SmbOpenAndXReq*)nb_ptr);
1055 }
1056
1057 dce2_move(nb_ptr, nb_len, DCE2_ComInfoCommandSize(com_info));
1058
1059 if (unicode)
1060 pad = (nb_ptr - (const uint8_t*)smb_hdr) & 1;
1061
1062 if (nb_len < (pad + null_bytes))
1063 return DCE2_RET__ERROR;
1064
1065 dce2_move(nb_ptr, nb_len, pad);
1066
1067 // Samba allows chaining OpenAndX/NtCreateAndX so might have
1068 // already been set.
1069 if (ssd->cur_rtracker->file_name == nullptr)
1070 {
1071 ssd->cur_rtracker->file_name =
1072 get_smb_file_name(nb_ptr, nb_len, unicode, &ssd->cur_rtracker->file_name_size);
1073 }
1074 }
1075
1076 return DCE2_RET__SUCCESS;
1077 }
1078
1079 // SMB_COM_READ_ANDX
DCE2_SmbReadAndX(DCE2_SmbSsnData * ssd,const SmbNtHdr * smb_hdr,const DCE2_SmbComInfo * com_info,const uint8_t * nb_ptr,uint32_t nb_len)1080 DCE2_Ret DCE2_SmbReadAndX(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
1081 const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
1082 {
1083 if (!DCE2_ComInfoCanProcessCommand(com_info))
1084 return DCE2_RET__ERROR;
1085
1086 if (DCE2_ComInfoIsRequest(com_info))
1087 {
1088 DCE2_SmbFileTracker* ftracker =
1089 DCE2_SmbGetFileTracker(ssd, SmbReadAndXReqFid((const SmbReadAndXReq*)nb_ptr));
1090
1091 // No sense in tracking response
1092 if (ftracker == nullptr)
1093 return DCE2_RET__ERROR;
1094
1095 if (!ftracker->is_ipc)
1096 ssd->cur_rtracker->file_offset = SmbReadAndXReqOffset((const SmbReadAndXExtReq*)nb_ptr);
1097
1098 // Set this for response
1099 ssd->cur_rtracker->ftracker = ftracker;
1100 }
1101 else
1102 {
1103 uint16_t com_size = DCE2_ComInfoCommandSize(com_info);
1104 uint16_t byte_count = DCE2_ComInfoByteCount(com_info);
1105 uint16_t doff = SmbReadAndXRespDataOff((const SmbReadAndXResp*)nb_ptr);
1106 uint32_t dcnt = SmbReadAndXRespDataCnt((const SmbReadAndXResp*)nb_ptr);
1107
1108 dce2_move(nb_ptr, nb_len, com_size);
1109
1110 if (DCE2_SmbCheckData(ssd, (const uint8_t*)smb_hdr, nb_ptr, nb_len,
1111 byte_count, dcnt, doff) != DCE2_RET__SUCCESS)
1112 return DCE2_RET__ERROR;
1113
1114 // This may move backwards
1115 dce2_move(nb_ptr, nb_len, ((const uint8_t*)smb_hdr + doff) - nb_ptr);
1116
1117 if (dcnt > nb_len)
1118 dcnt = nb_len;
1119
1120 return DCE2_SmbProcessResponseData(ssd, nb_ptr, dcnt);
1121 }
1122
1123 return DCE2_RET__SUCCESS;
1124 }
1125
1126 // SMB_COM_WRITE_ANDX
DCE2_SmbWriteAndX(DCE2_SmbSsnData * ssd,const SmbNtHdr * smb_hdr,const DCE2_SmbComInfo * com_info,const uint8_t * nb_ptr,uint32_t nb_len)1127 DCE2_Ret DCE2_SmbWriteAndX(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
1128 const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
1129 {
1130 if (!DCE2_ComInfoCanProcessCommand(com_info))
1131 {
1132 DCE2_SmbFileTracker* ftracker = ssd->cur_rtracker->ftracker;
1133
1134 if ((ftracker != nullptr) && ftracker->is_ipc
1135 && (ftracker->fp_writex_raw != nullptr))
1136 {
1137 ftracker->fp_writex_raw->remaining = 0;
1138 DCE2_BufferEmpty(ftracker->fp_writex_raw->buf);
1139 }
1140
1141 return DCE2_RET__ERROR;
1142 }
1143
1144 if (DCE2_ComInfoIsRequest(com_info)
1145 && (SmbWriteAndXReqStartRaw((const SmbWriteAndXReq*)nb_ptr)
1146 || SmbWriteAndXReqRaw((const SmbWriteAndXReq*)nb_ptr)))
1147 {
1148 DCE2_SmbFileTracker* ftracker =
1149 DCE2_SmbGetFileTracker(ssd, SmbWriteAndXReqFid((const SmbWriteAndXReq*)nb_ptr));
1150
1151 // Raw mode is only applicable to named pipes.
1152 if ((ftracker != nullptr) && ftracker->is_ipc)
1153 return DCE2_SmbWriteAndXRawRequest(ssd, smb_hdr, com_info, nb_ptr, nb_len);
1154 }
1155
1156 if (DCE2_ComInfoIsRequest(com_info))
1157 {
1158 uint16_t com_size = DCE2_ComInfoCommandSize(com_info);
1159 uint16_t byte_count = DCE2_ComInfoByteCount(com_info);
1160 uint16_t fid = SmbWriteAndXReqFid((const SmbWriteAndXReq*)nb_ptr);
1161 uint16_t doff = SmbWriteAndXReqDataOff((const SmbWriteAndXReq*)nb_ptr);
1162 uint32_t dcnt = SmbWriteAndXReqDataCnt((const SmbWriteAndXReq*)nb_ptr);
1163 uint64_t offset = SmbWriteAndXReqOffset((const SmbWriteAndXExtReq*)nb_ptr);
1164
1165 dce2_move(nb_ptr, nb_len, com_size);
1166
1167 if (DCE2_SmbCheckData(ssd, (const uint8_t*)smb_hdr, nb_ptr, nb_len,
1168 byte_count, dcnt, doff) != DCE2_RET__SUCCESS)
1169 return DCE2_RET__ERROR;
1170
1171 // This may move backwards
1172 dce2_move(nb_ptr, nb_len, ((const uint8_t*)smb_hdr + doff) - nb_ptr);
1173
1174 if (dcnt > nb_len)
1175 {
1176 // Current Samba errors if data count is greater than data left
1177 if (DCE2_SsnGetPolicy(&ssd->sd) == DCE2_POLICY__SAMBA)
1178 return DCE2_RET__ERROR;
1179
1180 // Windows and early Samba just use what's left
1181 dcnt = nb_len;
1182 }
1183
1184 return DCE2_SmbProcessRequestData(ssd, fid, nb_ptr, dcnt, offset);
1185 }
1186
1187 return DCE2_RET__SUCCESS;
1188 }
1189
1190 // SMB_COM_SESSION_SETUP_ANDX
DCE2_SmbSessionSetupAndX(DCE2_SmbSsnData * ssd,const SmbNtHdr * smb_hdr,const DCE2_SmbComInfo * com_info,const uint8_t * nb_ptr,uint32_t nb_len)1191 DCE2_Ret DCE2_SmbSessionSetupAndX(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
1192 const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
1193 {
1194 if (!DCE2_ComInfoCanProcessCommand(com_info))
1195 return DCE2_RET__ERROR;
1196
1197 if (DCE2_ComInfoIsRequest(com_info))
1198 {
1199 uint16_t max_multiplex =
1200 SmbSessionSetupAndXReqMaxMultiplex((const SmbLm10_SessionSetupAndXReq*)nb_ptr);
1201
1202 if (max_multiplex < ssd->max_outstanding_requests)
1203 ssd->max_outstanding_requests = max_multiplex;
1204
1205 if (!DCE2_SmbFingerprintedClient(ssd) && DCE2_GcSmbFingerprintClient(
1206 (dce2SmbProtoConf*)ssd->sd.config))
1207 {
1208 uint8_t increment = SmbUnicode(smb_hdr) ? 2 : 1;
1209 uint16_t word_count = DCE2_ComInfoWordCount(com_info);
1210 uint16_t com_size = DCE2_ComInfoCommandSize(com_info);
1211 uint32_t i;
1212
1213 DCE2_SmbSetFingerprintedClient(ssd);
1214
1215 // OS and Lanman strings won't be in request
1216 if ((word_count != 13) && (word_count != 12))
1217 return DCE2_RET__SUCCESS;
1218
1219 if (word_count == 13)
1220 {
1221 uint16_t oem_pass_len =
1222 SmbNt10SessionSetupAndXReqOemPassLen((const SmbNt10_SessionSetupAndXReq*)nb_ptr);
1223 uint16_t uni_pass_len =
1224 SmbNt10SessionSetupAndXReqUnicodePassLen((const SmbNt10_SessionSetupAndXReq*)nb_ptr);
1225
1226 dce2_move(nb_ptr, nb_len, com_size);
1227
1228 if (((uint32_t)oem_pass_len + uni_pass_len) > nb_len)
1229 {
1230 return DCE2_RET__ERROR;
1231 }
1232
1233 dce2_move(nb_ptr, nb_len, (oem_pass_len + uni_pass_len));
1234
1235 // If unicode there should be a padding byte if the password
1236 // lengths are even since the command length is odd
1237 if ((increment == 2) && (nb_len != 0) && !((oem_pass_len + uni_pass_len) & 1))
1238 dce2_move(nb_ptr, nb_len, 1);
1239 }
1240 else // Extended security blob version, word count of 12
1241 {
1242 uint16_t blob_len =
1243 SmbSessionSetupAndXReqBlobLen((const SmbNt10_SessionSetupAndXExtReq*)nb_ptr);
1244
1245 dce2_move(nb_ptr, nb_len, com_size);
1246
1247 if (blob_len > nb_len)
1248 {
1249 return DCE2_RET__ERROR;
1250 }
1251
1252 dce2_move(nb_ptr, nb_len, blob_len);
1253
1254 // If unicode there should be a padding byte if the blob
1255 // length is even since the command length is odd
1256 if ((increment == 2) && (nb_len != 0) && !(blob_len & 1))
1257 dce2_move(nb_ptr, nb_len, 1);
1258 }
1259
1260 // Attempting to fingerprint Client Windows/Samba version.
1261 // Move past Account and Domain strings
1262 // Blob version doesn't have these as they're in the blob
1263 if (DCE2_ComInfoWordCount(com_info) == 13)
1264 {
1265 int j;
1266
1267 for (j = 0; j < 2; j++)
1268 {
1269 assert(nb_ptr != nullptr);
1270 while ((nb_len >= increment) && (*nb_ptr != '\0'))
1271 dce2_move(nb_ptr, nb_len, increment);
1272
1273 // Just return success if we run out of data
1274 if (nb_len < increment)
1275 {
1276 return DCE2_RET__SUCCESS;
1277 }
1278
1279 // Move past null string terminator
1280 dce2_move(nb_ptr, nb_len, increment);
1281 }
1282 }
1283
1284 if (nb_len < increment)
1285 {
1286 return DCE2_RET__SUCCESS;
1287 }
1288
1289 // Note the below is quick and dirty. We're assuming the client
1290 // is kosher. It's policy will be used when the server is
1291 // sending data to it.
1292
1293 // Windows Vista and above don't put anything here
1294 assert(nb_ptr != nullptr);
1295 if (*nb_ptr == '\0')
1296 {
1297 DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__WINVISTA);
1298 return DCE2_RET__SUCCESS;
1299 }
1300
1301 // Windows
1302 if (*nb_ptr == 'W')
1303 {
1304 int state = OS_0;
1305 int64_t rlen = (int64_t)nb_len;
1306
1307 while ((rlen > 0) && (state < OS_FS))
1308 {
1309 if (dce2_smb_os_fsm[state].input == (char)*nb_ptr)
1310 {
1311 state = dce2_smb_os_fsm[state].next_state;
1312 dce2_move(nb_ptr, rlen, increment);
1313 }
1314 else
1315 {
1316 state = dce2_smb_os_fsm[state].fail_state;
1317 }
1318 }
1319
1320 switch (state)
1321 {
1322 case OS_WIN2000:
1323 DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__WIN2000);
1324 break;
1325 case OS_WINXP:
1326 DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__WINXP);
1327 break;
1328 case OS_WIN2003:
1329 DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__WIN2003);
1330 break;
1331 default:
1332 break;
1333 }
1334
1335 return DCE2_RET__SUCCESS;
1336 }
1337
1338 // Samba puts "Unix" in the OS field
1339 if (*nb_ptr != 'U')
1340 {
1341 return DCE2_RET__SUCCESS;
1342 }
1343
1344 // Move past OS string
1345 for (i = 0; (i < nb_len) && (nb_ptr[i] != '\0'); i += increment)
1346 ;
1347
1348 if ((i + increment) >= nb_len)
1349 {
1350 return DCE2_RET__SUCCESS;
1351 }
1352
1353 // Move to LanMan string
1354 dce2_move(nb_ptr, nb_len, i + increment);
1355
1356 // Samba
1357 if (*nb_ptr == 'S')
1358 {
1359 DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__SAMBA);
1360 }
1361 }
1362 }
1363 else
1364 {
1365 uint16_t uid = SmbUid(smb_hdr);
1366
1367 DCE2_SmbInsertUid(ssd, uid);
1368 ssd->cur_rtracker->uid = uid; // Set this in case there are chained commands
1369
1370 if (!(ssd->ssn_state_flags & DCE2_SMB_SSN_STATE__NEGOTIATED))
1371 ssd->ssn_state_flags |= DCE2_SMB_SSN_STATE__NEGOTIATED;
1372
1373 if (!DCE2_SmbFingerprintedServer(ssd) && DCE2_GcSmbFingerprintServer(
1374 (dce2SmbProtoConf*)ssd->sd.config))
1375 {
1376 uint8_t increment = SmbUnicode(smb_hdr) ? 2 : 1;
1377 uint32_t i;
1378
1379 DCE2_SmbSetFingerprintedServer(ssd);
1380
1381 // Set the policy based on what the server reports in the OS field
1382 // for Windows and the LanManager field for Samba
1383
1384 if (DCE2_ComInfoByteCount(com_info) == 0)
1385 return DCE2_RET__SUCCESS;
1386
1387 if (DCE2_ComInfoWordCount(com_info) == 3)
1388 {
1389 dce2_move(nb_ptr, nb_len, DCE2_ComInfoCommandSize(com_info));
1390
1391 // Word count 3 and Unicode has a one byte pad
1392 if ((increment == 2) && (nb_len != 0))
1393 dce2_move(nb_ptr, nb_len, 1);
1394 }
1395 else // Only valid word counts are 3 and 4
1396 {
1397 uint16_t blob_len = SmbSessionSetupAndXRespBlobLen(
1398 (const SmbNt10_SessionSetupAndXExtResp*)nb_ptr);
1399
1400 dce2_move(nb_ptr, nb_len, DCE2_ComInfoCommandSize(com_info));
1401
1402 if (blob_len > nb_len)
1403 {
1404 return DCE2_RET__ERROR;
1405 }
1406
1407 dce2_move(nb_ptr, nb_len, blob_len);
1408
1409 if ((increment == 2) && (nb_len != 0) && !(blob_len & 1))
1410 dce2_move(nb_ptr, nb_len, 1);
1411 }
1412
1413 // Attempting to fingerprint Server Windows/Samba version.
1414 // Note the below is quick and dirty. We're assuming the server
1415 // is kosher. It's policy will be used when the client is
1416 // sending data to it.
1417 assert(nb_ptr != nullptr);
1418 if ((nb_len < increment) || (*nb_ptr == '\0'))
1419 {
1420 return DCE2_RET__SUCCESS;
1421 }
1422
1423 // Windows
1424 if (*nb_ptr == 'W')
1425 {
1426 int state = OS_0;
1427 int64_t rlen = (int64_t)nb_len;
1428
1429 while ((rlen > 0) && (state < OS_FS))
1430 {
1431 if (dce2_smb_os_fsm[state].input == (char)*nb_ptr)
1432 {
1433 state = dce2_smb_os_fsm[state].next_state;
1434 dce2_move(nb_ptr, rlen, increment);
1435 }
1436 else
1437 {
1438 state = dce2_smb_os_fsm[state].fail_state;
1439 }
1440 }
1441
1442 switch (state)
1443 {
1444 case OS_WIN2000:
1445 DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__WIN2000);
1446 break;
1447 case OS_WINXP:
1448 DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__WINXP);
1449 break;
1450 case OS_WIN2003:
1451 DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__WIN2003);
1452 break;
1453 case OS_WIN2008:
1454 DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__WIN2008);
1455 break;
1456 case OS_WINVISTA:
1457 DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__WINVISTA);
1458 break;
1459 case OS_WIN7:
1460 DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__WIN7);
1461 break;
1462 default:
1463 break;
1464 }
1465
1466 return DCE2_RET__SUCCESS;
1467 }
1468
1469 // Samba puts "Unix" in the OS field
1470 if (*nb_ptr != 'U')
1471 {
1472 return DCE2_RET__SUCCESS;
1473 }
1474
1475 // Move past OS string
1476 for (i = 0; (i < nb_len) && (nb_ptr[i] != '\0'); i += increment)
1477 ;
1478
1479 if ((i + increment) >= nb_len)
1480 {
1481 return DCE2_RET__SUCCESS;
1482 }
1483
1484 // Move to LanMan string
1485 dce2_move(nb_ptr, nb_len, i + increment);
1486
1487 // Samba
1488 if (*nb_ptr == 'S')
1489 {
1490 uint8_t r1 = 0; // Release version first digit
1491 uint8_t r2 = 0; // Release version second digit
1492
1493 // Get Major version
1494 for (i = 0; (i < nb_len) && (*nb_ptr != '\0'); i += increment)
1495 {
1496 if (isdigit((int)nb_ptr[i]))
1497 break;
1498 }
1499
1500 if ((i == nb_len) || (*nb_ptr == '\0'))
1501 {
1502 return DCE2_RET__SUCCESS;
1503 }
1504
1505 // If less than 3 set policy to earliest Samba policy we use
1506 if ((nb_ptr[i] == '0') || (nb_ptr[i] == '1') || (nb_ptr[i] == '2'))
1507 {
1508 DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__SAMBA_3_0_20);
1509 return DCE2_RET__SUCCESS;
1510 }
1511
1512 // Need ".\d.\d\d" or ".\d.\d\x00"
1513 if (i + increment*5 > nb_len)
1514 {
1515 return DCE2_RET__SUCCESS;
1516 }
1517
1518 i += increment*2;
1519
1520 // If it's not 0, then set to latest Samba policy we use
1521 if (nb_ptr[i] != '0')
1522 {
1523 DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__SAMBA);
1524 return DCE2_RET__SUCCESS;
1525 }
1526
1527 r1 = nb_ptr[i + increment*2];
1528 r2 = nb_ptr[i + increment*3];
1529
1530 // First digit is 1 or no second digit or 20, Samba 3.0.20
1531 if ((r1 == '1') || (r2 == '\0') || ((r1 == '2') && (r2 == '0')))
1532 {
1533 DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__SAMBA_3_0_20);
1534 return DCE2_RET__SUCCESS;
1535 }
1536
1537 // 21 or 22, Samba 3.0.22
1538 if ((r1 == '2') && (r2 <= '2'))
1539 {
1540 DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__SAMBA_3_0_22);
1541 return DCE2_RET__SUCCESS;
1542 }
1543
1544 // 23, 24 ... 30 ... 37, Samba 3.0.37
1545 if ((r1 == '2') || ((r1 == '3') && (r2 <= '7')))
1546 {
1547 DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__SAMBA_3_0_37);
1548 return DCE2_RET__SUCCESS;
1549 }
1550
1551 DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__SAMBA);
1552 }
1553 }
1554 }
1555
1556 return DCE2_RET__SUCCESS;
1557 }
1558
1559 // SMB_COM_NEGOTIATE
DCE2_SmbNegotiate(DCE2_SmbSsnData * ssd,const SmbNtHdr *,const DCE2_SmbComInfo * com_info,const uint8_t * nb_ptr,uint32_t nb_len)1560 DCE2_Ret DCE2_SmbNegotiate(DCE2_SmbSsnData* ssd, const SmbNtHdr*,
1561 const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
1562 {
1563 if (!DCE2_ComInfoCanProcessCommand(com_info))
1564 return DCE2_RET__ERROR;
1565
1566 if (DCE2_ComInfoIsRequest(com_info))
1567 {
1568 // Have at least 2 bytes based on byte count check done earlier
1569 const uint8_t* term_ptr;
1570 int ntlm_index = 0;
1571 uint16_t com_size = DCE2_ComInfoCommandSize(com_info);
1572
1573 dce2_move(nb_ptr, nb_len, com_size);
1574
1575 while ((term_ptr = (const uint8_t*)memchr(nb_ptr, '\0', nb_len)) != nullptr)
1576 {
1577 if (!SmbFmtDialect(*nb_ptr))
1578 {
1579 dce_alert(GID_DCE2, DCE2_SMB_BAD_FORM, (dce2CommonStats*)&dce2_smb_stats,
1580 ssd->sd);
1581
1582 // Windows errors if bad format
1583 if (DCE2_SsnIsWindowsPolicy(&ssd->sd))
1584 {
1585 return DCE2_RET__ERROR;
1586 }
1587 }
1588
1589 // Move past format
1590 dce2_move(nb_ptr, nb_len, 1);
1591
1592 if (nb_len == 0)
1593 break;
1594
1595 // Just a null byte - acceptable by Samba and Windows
1596 if (term_ptr == nb_ptr)
1597 continue;
1598
1599 if ((*nb_ptr == 'N')
1600 && (strncmp((const char*)nb_ptr, SMB_DIALECT_NT_LM_012, term_ptr - nb_ptr) == 0))
1601 break;
1602
1603 // Move past string and null byte
1604 dce2_move(nb_ptr, nb_len, (term_ptr - nb_ptr) + 1);
1605
1606 ntlm_index++;
1607 }
1608
1609 if (term_ptr != nullptr)
1610 {
1611 ssd->dialect_index = ntlm_index;
1612 }
1613 else
1614 {
1615 ssd->dialect_index = DCE2_SENTINEL;
1616 dce_alert(GID_DCE2, DCE2_SMB_DEPR_DIALECT_NEGOTIATED,
1617 (dce2CommonStats*)&dce2_smb_stats, ssd->sd);
1618 }
1619 }
1620 else
1621 {
1622 const uint16_t dialect_index =
1623 SmbNegotiateRespDialectIndex((const SmbCore_NegotiateProtocolResp*)nb_ptr);
1624
1625 if ((ssd->dialect_index != DCE2_SENTINEL) && (dialect_index != ssd->dialect_index))
1626 dce_alert(GID_DCE2, DCE2_SMB_DEPR_DIALECT_NEGOTIATED,
1627 (dce2CommonStats*)&dce2_smb_stats, ssd->sd);
1628
1629 ssd->ssn_state_flags |= DCE2_SMB_SSN_STATE__NEGOTIATED;
1630
1631 if (DCE2_ComInfoWordCount(com_info) == 17)
1632 {
1633 ssd->max_outstanding_requests =
1634 SmbNt_NegotiateRespMaxMultiplex((const SmbNt_NegotiateProtocolResp*)nb_ptr);
1635 }
1636 else if (DCE2_ComInfoWordCount(com_info) == 13)
1637 {
1638 ssd->max_outstanding_requests =
1639 SmbLm_NegotiateRespMaxMultiplex((const SmbLm10_NegotiateProtocolResp*)nb_ptr);
1640 }
1641 else
1642 {
1643 ssd->max_outstanding_requests = 1;
1644 }
1645 }
1646
1647 return DCE2_RET__SUCCESS;
1648 }
1649
1650 // SMB_COM_TREE_CONNECT_ANDX
DCE2_SmbTreeConnectAndX(DCE2_SmbSsnData * ssd,const SmbNtHdr * smb_hdr,const DCE2_SmbComInfo * com_info,const uint8_t * nb_ptr,uint32_t nb_len)1651 DCE2_Ret DCE2_SmbTreeConnectAndX(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
1652 const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
1653 {
1654 uint16_t com_size = DCE2_ComInfoCommandSize(com_info);
1655
1656 if (!DCE2_ComInfoCanProcessCommand(com_info))
1657 return DCE2_RET__ERROR;
1658
1659 if (DCE2_ComInfoIsRequest(com_info))
1660 {
1661 if (DCE2_ScSmbInvalidShares((dce2SmbProtoConf*)ssd->sd.config) != nullptr)
1662 {
1663 uint16_t pass_len = SmbTreeConnectAndXReqPassLen((const SmbTreeConnectAndXReq*)nb_ptr);
1664 dce2_move(nb_ptr, nb_len, com_size);
1665 if (pass_len >= nb_len)
1666 return DCE2_RET__ERROR;
1667
1668 // Move past password length
1669 dce2_move(nb_ptr, nb_len, pass_len);
1670
1671 const uint8_t* bs = nullptr;
1672 // Move past path components
1673 assert(nb_ptr != nullptr);
1674 while ((bs = (const uint8_t*)memchr(nb_ptr, '\\', nb_len)) != nullptr)
1675 dce2_move(nb_ptr, nb_len, (bs - nb_ptr) + 1);
1676
1677 // Move past null byte if unicode
1678 if (SmbUnicode(smb_hdr) && (nb_len != 0))
1679 dce2_move(nb_ptr, nb_len, 1);
1680
1681 if (nb_len != 0)
1682 DCE2_SmbInvalidShareCheck(ssd, smb_hdr, nb_ptr, nb_len);
1683 }
1684 }
1685 else
1686 {
1687 dce2_move(nb_ptr, nb_len, com_size);
1688
1689 int state = SERVICE_0;
1690 while ((nb_len > 0) && (state < SERVICE_FS))
1691 {
1692 if (dce2_smb_service_fsm[state].input == (char)*nb_ptr)
1693 {
1694 state = dce2_smb_service_fsm[state].next_state;
1695 dce2_move(nb_ptr, nb_len, 1);
1696 }
1697 else
1698 {
1699 state = dce2_smb_service_fsm[state].fail_state;
1700 }
1701 }
1702
1703 uint16_t tid = SmbTid(smb_hdr);
1704 bool is_ipc = true;
1705 switch (state)
1706 {
1707 case SERVICE_IPC:
1708 break;
1709 case SERVICE_DISK:
1710 is_ipc = false;
1711 break;
1712 default:
1713 return DCE2_RET__IGNORE;
1714 }
1715
1716 // Insert tid into list
1717 DCE2_SmbInsertTid(ssd, tid, is_ipc);
1718 ssd->cur_rtracker->tid = tid; // Set this in case there are chained commands
1719 }
1720
1721 return DCE2_RET__SUCCESS;
1722 }
1723
1724 // SMB_COM_TREE_CONNECT
DCE2_SmbTreeConnect(DCE2_SmbSsnData * ssd,const SmbNtHdr * smb_hdr,const DCE2_SmbComInfo * com_info,const uint8_t * nb_ptr,uint32_t nb_len)1725 DCE2_Ret DCE2_SmbTreeConnect(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
1726 const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
1727 {
1728 if (!DCE2_ComInfoCanProcessCommand(com_info))
1729 return DCE2_RET__ERROR;
1730
1731 if (DCE2_ComInfoIsRequest(com_info))
1732 {
1733 uint16_t com_size = DCE2_ComInfoCommandSize(com_info);
1734
1735 // Have at least 4 bytes of data based on byte count check done earlier
1736
1737 dce2_move(nb_ptr, nb_len, com_size);
1738
1739 // If unicode flag is set, strings, except possibly the service string
1740 // are going to be unicode. The NT spec specifies that unicode strings
1741 // must be word aligned with respect to the beginning of the SMB and that for
1742 // type-prefixed strings (this case), the padding byte is found after the
1743 // type format byte.
1744
1745 // This byte will realign things.
1746 if (*nb_ptr != SMB_FMT__ASCII)
1747 {
1748 dce_alert(GID_DCE2, DCE2_SMB_BAD_FORM, (dce2CommonStats*)&dce2_smb_stats,
1749 ssd->sd);
1750 return DCE2_RET__ERROR;
1751 }
1752
1753 dce2_move(nb_ptr, nb_len, 1);
1754
1755 // IPC$ does not need to be case sensitive. And the case sensitivity flag in
1756 // the SMB header doesn't seem to have any effect on this.
1757 const uint8_t* bs = nullptr;
1758 while ((bs = (const uint8_t*)memchr(nb_ptr, '\\', nb_len)) != nullptr)
1759 dce2_move(nb_ptr, nb_len, (bs - nb_ptr) + 1);
1760
1761 bool unicode = SmbUnicode(smb_hdr);
1762 if (unicode && (nb_len > 0))
1763 dce2_move(nb_ptr, nb_len, 1);
1764
1765 // Check for invalid shares first
1766 if ((DCE2_ScSmbInvalidShares((dce2SmbProtoConf*)ssd->sd.config) != nullptr) && (nb_len >
1767 0))
1768 DCE2_SmbInvalidShareCheck(ssd, smb_hdr, nb_ptr, nb_len);
1769
1770 int state = SHARE_0;
1771 uint8_t increment = unicode ? 2 : 1;
1772 while ((nb_len >= increment) && (state < SHARE_FS))
1773 {
1774 if (dce2_ipc_share_fsm[state].input == toupper((int)nb_ptr[0]))
1775 {
1776 if (unicode && (nb_ptr[1] != 0))
1777 break;
1778 state = dce2_ipc_share_fsm[state].next_state;
1779 dce2_move(nb_ptr, nb_len, increment);
1780 }
1781 else
1782 {
1783 state = dce2_ipc_share_fsm[state].fail_state;
1784 }
1785 }
1786
1787 bool is_ipc = false;
1788 switch (state)
1789 {
1790 case SHARE_IPC:
1791 is_ipc = true;
1792 break;
1793 case SHARE_FS:
1794 default:
1795 break;
1796 }
1797
1798 ssd->cur_rtracker->is_ipc = is_ipc;
1799 }
1800 else
1801 {
1802 // FIXIT-L What if the TID in the SMB header differs from that returned
1803 // in the TreeConnect command response?
1804 uint16_t tid = SmbTid(smb_hdr);
1805 DCE2_SmbInsertTid(ssd, tid, ssd->cur_rtracker->is_ipc);
1806 }
1807
1808 return DCE2_RET__SUCCESS;
1809 }
1810
1811 // SMB_COM_NT_CREATE_ANDX
DCE2_SmbNtCreateAndX(DCE2_SmbSsnData * ssd,const SmbNtHdr * smb_hdr,const DCE2_SmbComInfo * com_info,const uint8_t * nb_ptr,uint32_t nb_len)1812 DCE2_Ret DCE2_SmbNtCreateAndX(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
1813 const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
1814 {
1815 if (!DCE2_ComInfoCanProcessCommand(com_info))
1816 return DCE2_RET__ERROR;
1817
1818 if (DCE2_ComInfoIsResponse(com_info))
1819 {
1820 const uint16_t fid = SmbNtCreateAndXRespFid((const SmbNtCreateAndXResp*)nb_ptr);
1821 DCE2_SmbFileTracker* ftracker = nullptr;
1822
1823 // Set request tracker's current file tracker in case of chained commands
1824 switch (SmbAndXCom2((const SmbAndXCommon*)nb_ptr))
1825 {
1826 // This is in case in the request a write was chained to an open
1827 // in which case the write will be to the newly opened file
1828 case SMB_COM_WRITE:
1829 case SMB_COM_WRITE_ANDX:
1830 case SMB_COM_TRANSACTION:
1831 case SMB_COM_READ_ANDX:
1832 ftracker = DCE2_SmbDequeueTmpFileTracker(ssd, ssd->cur_rtracker, fid);
1833 break;
1834 default:
1835 break;
1836 }
1837
1838 if (!DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid))
1839 {
1840 const bool is_directory = SmbNtCreateAndXRespDirectory((const SmbNtCreateAndXResp*)nb_ptr);
1841 const uint16_t resource_type =
1842 SmbNtCreateAndXRespResourceType((const SmbNtCreateAndXResp*)nb_ptr);
1843
1844 if (is_directory || !SmbResourceTypeDisk(resource_type))
1845 {
1846 if (ftracker != nullptr)
1847 DCE2_SmbRemoveFileTracker(ssd, ftracker);
1848 return DCE2_RET__SUCCESS;
1849 }
1850
1851 // Give preference to files opened with the sequential only flag set
1852 if (((ssd->fapi_ftracker == nullptr) || !ssd->fapi_ftracker->ff_sequential_only)
1853 && (ftracker == nullptr) && ssd->cur_rtracker->sequential_only)
1854 {
1855 DCE2_SmbAbortFileAPI(ssd);
1856 }
1857 }
1858
1859 if (ftracker == nullptr)
1860 {
1861 ftracker = DCE2_SmbNewFileTracker(ssd,
1862 ssd->cur_rtracker->uid, ssd->cur_rtracker->tid, fid);
1863 if (ftracker == nullptr)
1864 return DCE2_RET__ERROR;
1865 }
1866
1867 DCE2_Update_Ftracker_from_ReqTracker(ftracker, ssd->cur_rtracker);
1868
1869 if (!ftracker->is_ipc)
1870 {
1871 const uint32_t create_disposition =
1872 SmbNtCreateAndXRespCreateDisposition((const SmbNtCreateAndXResp*)nb_ptr);
1873
1874 if (SmbCreateDispositionRead(create_disposition))
1875 {
1876 ftracker->ff_file_size =
1877 SmbNtCreateAndXRespEndOfFile((const SmbNtCreateAndXResp*)nb_ptr);
1878 }
1879 else
1880 {
1881 ftracker->ff_file_size = ssd->cur_rtracker->file_size;
1882 ftracker->ff_file_direction = DCE2_SMB_FILE_DIRECTION__UPLOAD;
1883 }
1884
1885 ftracker->ff_sequential_only = ssd->cur_rtracker->sequential_only;
1886 }
1887
1888 ssd->cur_rtracker->ftracker = ftracker;
1889 }
1890 else
1891 {
1892 bool is_ipc = DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid);
1893 uint8_t smb_com2 = SmbAndXCom2((const SmbAndXCommon*)nb_ptr);
1894 uint16_t file_name_length =
1895 SmbNtCreateAndXReqFileNameLen((const SmbNtCreateAndXReq*)nb_ptr);
1896
1897 if (!is_ipc)
1898 {
1899 uint32_t ext_file_attrs =
1900 SmbNtCreateAndXReqFileAttrs((const SmbNtCreateAndXReq*)nb_ptr);
1901
1902 if (SmbEvasiveFileAttrs(ext_file_attrs))
1903 dce_alert(GID_DCE2, DCE2_SMB_EVASIVE_FILE_ATTRS,
1904 (dce2CommonStats*)&dce2_smb_stats, ssd->sd);
1905 // If the file is going to be accessed sequentially, track it.
1906 if (SmbNtCreateAndXReqSequentialOnly((const SmbNtCreateAndXReq*)nb_ptr))
1907 ssd->cur_rtracker->sequential_only = true;
1908
1909 ssd->cur_rtracker->file_size = SmbNtCreateAndXReqAllocSize(
1910 (const SmbNtCreateAndXReq*)nb_ptr);
1911 }
1912
1913 dce2_move(nb_ptr, nb_len, DCE2_ComInfoCommandSize(com_info));
1914
1915 if (file_name_length > DCE2_SMB_MAX_PATH_LEN)
1916 return DCE2_RET__ERROR;
1917
1918 uint32_t pad = 0;
1919 const bool unicode = SmbUnicode(smb_hdr);
1920 if (unicode)
1921 pad = (nb_ptr - (const uint8_t*)smb_hdr) & 1;
1922
1923 if (nb_len < (pad + file_name_length))
1924 return DCE2_RET__ERROR;
1925
1926 dce2_move(nb_ptr, nb_len, pad);
1927
1928 // Samba allows chaining OpenAndX/NtCreateAndX so might have
1929 // already been set.
1930 if (ssd->cur_rtracker->file_name == nullptr)
1931 {
1932 ssd->cur_rtracker->file_name =
1933 get_smb_file_name(nb_ptr, file_name_length, unicode,
1934 &ssd->cur_rtracker->file_name_size);
1935 }
1936
1937 if (is_ipc)
1938 {
1939 switch (smb_com2)
1940 {
1941 case SMB_COM_READ_ANDX:
1942 if (DCE2_SsnIsWindowsPolicy(&ssd->sd))
1943 return DCE2_RET__ERROR;
1944 break;
1945 default:
1946 break;
1947 }
1948 }
1949 }
1950
1951 return DCE2_RET__SUCCESS;
1952 }
1953
1954 // SMB_COM_TREE_DISCONNECT
DCE2_SmbTreeDisconnect(DCE2_SmbSsnData * ssd,const SmbNtHdr *,const DCE2_SmbComInfo * com_info,const uint8_t *,uint32_t)1955 DCE2_Ret DCE2_SmbTreeDisconnect(DCE2_SmbSsnData* ssd, const SmbNtHdr*,
1956 const DCE2_SmbComInfo* com_info, const uint8_t*, uint32_t)
1957 {
1958 if (!DCE2_ComInfoCanProcessCommand(com_info))
1959 return DCE2_RET__ERROR;
1960
1961 if (DCE2_ComInfoIsResponse(com_info))
1962 DCE2_SmbRemoveTid(ssd, ssd->cur_rtracker->tid);
1963
1964 return DCE2_RET__SUCCESS;
1965 }
1966
1967 // SMB_COM_LOGOFF_ANDX
DCE2_SmbLogoffAndX(DCE2_SmbSsnData * ssd,const SmbNtHdr * smb_hdr,const DCE2_SmbComInfo * com_info,const uint8_t *,uint32_t)1968 DCE2_Ret DCE2_SmbLogoffAndX(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
1969 const DCE2_SmbComInfo* com_info, const uint8_t*, uint32_t)
1970 {
1971 if (!DCE2_ComInfoCanProcessCommand(com_info))
1972 return DCE2_RET__ERROR;
1973
1974 if (DCE2_ComInfoIsResponse(com_info))
1975 {
1976 DCE2_SmbRemoveUid(ssd, ssd->cur_rtracker->uid);
1977
1978 switch (DCE2_SsnGetServerPolicy(&ssd->sd))
1979 {
1980 case DCE2_POLICY__WIN2000:
1981 case DCE2_POLICY__WINXP:
1982 case DCE2_POLICY__WINVISTA:
1983 case DCE2_POLICY__WIN2003:
1984 case DCE2_POLICY__WIN2008:
1985 case DCE2_POLICY__WIN7:
1986 /* Windows responds to a chained LogoffAndX => SessionSetupAndX with a
1987 * word count 3 LogoffAndX without the chained SessionSetupAndX */
1988 if (DCE2_ComInfoWordCount(com_info) == 3)
1989 {
1990 uint16_t uid = SmbUid(smb_hdr);
1991 DCE2_SmbInsertUid(ssd, uid);
1992 ssd->cur_rtracker->uid = uid; // Set this in case there are chained commands
1993 }
1994 break;
1995 default:
1996 break;
1997 }
1998 }
1999
2000 return DCE2_RET__SUCCESS;
2001 }
2002
2003 // SMB_COM_READ_RAW
DCE2_SmbReadRaw(DCE2_SmbSsnData * ssd,const SmbNtHdr *,const DCE2_SmbComInfo * com_info,const uint8_t * nb_ptr,uint32_t)2004 DCE2_Ret DCE2_SmbReadRaw(DCE2_SmbSsnData* ssd, const SmbNtHdr*,
2005 const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t)
2006 {
2007 if (!DCE2_ComInfoCanProcessCommand(com_info))
2008 return DCE2_RET__ERROR;
2009
2010 if (DCE2_ComInfoIsRequest(com_info))
2011 {
2012 DCE2_SmbFileTracker* ftracker =
2013 DCE2_SmbFindFileTracker(ssd, ssd->cur_rtracker->uid,
2014 ssd->cur_rtracker->tid, SmbReadRawReqFid((const SmbReadRawReq*)nb_ptr));
2015
2016 ssd->cur_rtracker->ftracker = ftracker;
2017 ssd->pdu_state = DCE2_SMB_PDU_STATE__RAW_DATA;
2018 if ((ftracker != nullptr) && !ftracker->is_ipc)
2019 ssd->cur_rtracker->file_offset = SmbReadRawReqOffset((const SmbReadRawExtReq*)nb_ptr);
2020 }
2021 else
2022 {
2023 // The server response is the raw data. Supposedly if an error occurs,
2024 // the server will send a 0 byte read. Just the NetBIOS header with
2025 // zero byte length. Client upon getting the zero read is supposed to issue
2026 // another read using ReadAndX or Read to get the error.
2027 return DCE2_RET__ERROR;
2028 }
2029
2030 return DCE2_RET__SUCCESS;
2031 }
2032
2033 // SMB_COM_WRITE_RAW
DCE2_SmbWriteRaw(DCE2_SmbSsnData * ssd,const SmbNtHdr * smb_hdr,const DCE2_SmbComInfo * com_info,const uint8_t * nb_ptr,uint32_t nb_len)2034 DCE2_Ret DCE2_SmbWriteRaw(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
2035 const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
2036 {
2037 if (!DCE2_ComInfoCanProcessCommand(com_info))
2038 return DCE2_RET__ERROR;
2039
2040 if (DCE2_ComInfoIsRequest(com_info))
2041 {
2042 uint16_t com_size = DCE2_ComInfoCommandSize(com_info);
2043 uint16_t byte_count = DCE2_ComInfoByteCount(com_info);
2044 uint16_t fid = SmbWriteRawReqFid((const SmbWriteRawReq*)nb_ptr);
2045 uint16_t tdcnt = SmbWriteRawReqTotalCount((const SmbWriteRawReq*)nb_ptr);
2046 bool writethrough = SmbWriteRawReqWriteThrough((const SmbWriteRawReq*)nb_ptr);
2047 uint16_t doff = SmbWriteRawReqDataOff((const SmbWriteRawReq*)nb_ptr);
2048 uint16_t dcnt = SmbWriteRawReqDataCnt((const SmbWriteRawReq*)nb_ptr);
2049 uint64_t offset = SmbWriteRawReqOffset((const SmbWriteRawExtReq*)nb_ptr);
2050
2051 dce2_move(nb_ptr, nb_len, com_size);
2052
2053 if (DCE2_SmbCheckTotalCount(ssd, tdcnt, dcnt, 0) != DCE2_RET__SUCCESS)
2054 return DCE2_RET__ERROR;
2055
2056 if (DCE2_SmbCheckData(ssd, (const uint8_t*)smb_hdr, nb_ptr, nb_len,
2057 byte_count, dcnt, doff) != DCE2_RET__SUCCESS)
2058 return DCE2_RET__ERROR;
2059
2060 // This may move backwards
2061 dce2_move(nb_ptr, nb_len, ((const uint8_t*)smb_hdr + doff) - nb_ptr);
2062
2063 if (dcnt > nb_len)
2064 {
2065 dce_alert(GID_DCE2, DCE2_SMB_NB_LT_DSIZE, (dce2CommonStats*)&dce2_smb_stats,
2066 ssd->sd);
2067 return DCE2_RET__ERROR;
2068 }
2069
2070 // If all of the data wasn't written in this request, the server will
2071 // send an interim SMB_COM_WRITE_RAW response and the client will send
2072 // the rest of the data raw. In this case if the WriteThrough flag is
2073 // not set, the server will not send a final SMB_COM_WRITE_COMPLETE
2074 // response. If all of the data is in this request the server will
2075 // send an SMB_COM_WRITE_COMPLETE response regardless of whether or
2076 // not the WriteThrough flag is set.
2077 if (dcnt != tdcnt)
2078 {
2079 ssd->cur_rtracker->writeraw_writethrough = writethrough;
2080 ssd->cur_rtracker->writeraw_remaining = tdcnt - dcnt;
2081 }
2082
2083 return DCE2_SmbProcessRequestData(ssd, fid, nb_ptr, dcnt, offset);
2084 }
2085 else
2086 {
2087 DCE2_Policy policy = DCE2_SsnGetServerPolicy(&ssd->sd);
2088
2089 // Samba messes this up and sends a request instead of an interim
2090 // response and a response instead of a Write Complete response.
2091 switch (policy)
2092 {
2093 case DCE2_POLICY__SAMBA:
2094 case DCE2_POLICY__SAMBA_3_0_37:
2095 case DCE2_POLICY__SAMBA_3_0_22:
2096 case DCE2_POLICY__SAMBA_3_0_20:
2097 if (SmbType(smb_hdr) != SMB_TYPE__REQUEST)
2098 return DCE2_RET__SUCCESS;
2099 break;
2100 default:
2101 break;
2102 }
2103
2104 // If all the data wasn't written initially this interim response will
2105 // be sent by the server and the raw data will ensue from the client.
2106 ssd->pdu_state = DCE2_SMB_PDU_STATE__RAW_DATA;
2107 }
2108
2109 return DCE2_RET__SUCCESS;
2110 }
2111
2112 // SMB_COM_WRITE_COMPLETE
DCE2_SmbWriteComplete(DCE2_SmbSsnData *,const SmbNtHdr *,const DCE2_SmbComInfo * com_info,const uint8_t *,uint32_t)2113 DCE2_Ret DCE2_SmbWriteComplete(DCE2_SmbSsnData*, const SmbNtHdr*,
2114 const DCE2_SmbComInfo* com_info, const uint8_t*, uint32_t)
2115 {
2116 if (!DCE2_ComInfoCanProcessCommand(com_info))
2117 return DCE2_RET__ERROR;
2118
2119 return DCE2_RET__SUCCESS;
2120 }
2121
2122 // SMB_COM_WRITE_AND_CLOSE
DCE2_SmbWriteAndClose(DCE2_SmbSsnData * ssd,const SmbNtHdr * smb_hdr,const DCE2_SmbComInfo * com_info,const uint8_t * nb_ptr,uint32_t nb_len)2123 DCE2_Ret DCE2_SmbWriteAndClose(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr,
2124 const DCE2_SmbComInfo* com_info, const uint8_t* nb_ptr, uint32_t nb_len)
2125 {
2126 if (!DCE2_ComInfoCanProcessCommand(com_info))
2127 return DCE2_RET__ERROR;
2128
2129 if (DCE2_ComInfoIsRequest(com_info))
2130 {
2131 // Have at least one byte based on byte count check done earlier
2132
2133 uint16_t com_size = DCE2_ComInfoCommandSize(com_info);
2134 uint16_t byte_count = DCE2_ComInfoByteCount(com_info);
2135 uint16_t dcnt = SmbWriteAndCloseReqCount((const SmbWriteAndCloseReq*)nb_ptr);
2136 uint16_t fid = SmbWriteAndCloseReqFid((const SmbWriteAndCloseReq*)nb_ptr);
2137 uint32_t offset = SmbWriteAndCloseReqOffset((const SmbWriteAndCloseReq*)nb_ptr);
2138
2139 dce2_move(nb_ptr, nb_len, (com_size + 1));
2140
2141 if (DCE2_SmbCheckData(ssd, (const uint8_t*)smb_hdr, nb_ptr, nb_len,
2142 byte_count, dcnt,
2143 (uint16_t)(sizeof(SmbNtHdr) + com_size + 1)) != DCE2_RET__SUCCESS)
2144 return DCE2_RET__ERROR;
2145
2146 if (dcnt == 0)
2147 {
2148 dce_alert(GID_DCE2, DCE2_SMB_DCNT_ZERO, (dce2CommonStats*)&dce2_smb_stats,
2149 ssd->sd);
2150 return DCE2_RET__ERROR;
2151 }
2152
2153 // WriteAndClose has a 1 byte pad after the byte count
2154 if ((uint32_t)(dcnt + 1) != (uint32_t)byte_count)
2155 dce_alert(GID_DCE2, DCE2_SMB_INVALID_DSIZE, (dce2CommonStats*)&dce2_smb_stats,
2156 ssd->sd);
2157
2158 if (dcnt > nb_len)
2159 dcnt = (uint16_t)nb_len;
2160
2161 return DCE2_SmbProcessRequestData(ssd, fid, nb_ptr, dcnt, offset);
2162 }
2163 else
2164 {
2165 DCE2_SmbRemoveFileTracker(ssd, ssd->cur_rtracker->ftracker);
2166 }
2167
2168 return DCE2_RET__SUCCESS;
2169 }
2170