1 /****************************************************************************
2 * Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
3 * Copyright (C) 2008-2013 Sourcefire, Inc.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License Version 2 as
7 * published by the Free Software Foundation. You may not use, modify or
8 * distribute this program under any other version of the GNU General
9 * Public License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 *
20 ****************************************************************************
21 * Module for handling connection-oriented DCE/RPC processing. Provides
22 * context id, interface UUID correlation and tracking for use with the
23 * preprocessor rule options. Provides desegmentation and defragmentation.
24 * Sets appropriate data for use with the preprocessor rule options.
25 *
26 * 8/17/2008 - Initial implementation ... Todd Wease <twease@sourcefire.com>
27 *
28 ****************************************************************************/
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #include "sf_types.h"
35 #include "spp_dce2.h"
36 #include "dce2_co.h"
37 #include "dce2_tcp.h"
38 #include "dce2_smb.h"
39 #include "snort_dce2.h"
40 #include "dce2_memory.h"
41 #include "dce2_utils.h"
42 #include "dce2_stats.h"
43 #include "dce2_event.h"
44 #include "dce2_debug.h"
45 #include "dcerpc.h"
46 #include "profiler.h"
47 #include "sf_snort_packet.h"
48 #include "sf_dynamic_preprocessor.h"
49
50 /********************************************************************
51 * Macros
52 ********************************************************************/
53 #define DCE2_CO__MIN_ALLOC_SIZE 50
54 #define DCE2_MAX_XMIT_SIZE_FUZZ 500
55
56 /********************************************************************
57 * Global variables
58 ********************************************************************/
59 static int co_reassembled = 0;
60
61 /********************************************************************
62 * Enumerations
63 ********************************************************************/
64 typedef enum _DCE2_CoRpktType
65 {
66 DCE2_CO_RPKT_TYPE__SEG,
67 DCE2_CO_RPKT_TYPE__FRAG,
68 DCE2_CO_RPKT_TYPE__ALL
69
70 } DCE2_CoRpktType;
71
72 typedef enum _DCE2_CoCtxState
73 {
74 DCE2_CO_CTX_STATE__ACCEPTED,
75 DCE2_CO_CTX_STATE__REJECTED,
76 DCE2_CO_CTX_STATE__PENDING
77
78 } DCE2_CoCtxState;
79
80 /********************************************************************
81 * Structures
82 ********************************************************************/
83 typedef struct _DCE2_CoCtxIdNode
84 {
85 uint16_t ctx_id; /* The context id */
86 Uuid iface; /* The presentation syntax uuid for the interface */
87 uint16_t iface_vers_maj; /* The major version of the interface */
88 uint16_t iface_vers_min; /* The minor version of the interface */
89
90 /* Whether or not the server accepted or rejected the client bind/alter context
91 * request. Initially set to pending until server response */
92 DCE2_CoCtxState state;
93
94 } DCE2_CoCtxIdNode;
95
96 /********************************************************************
97 * Private function prototypes
98 ********************************************************************/
99 static DCE2_Ret DCE2_CoHdrChecks(DCE2_SsnData *, DCE2_CoTracker *, const DceRpcCoHdr *);
100 static void DCE2_CoDecode(DCE2_SsnData *, DCE2_CoTracker *,
101 const uint8_t *, uint16_t);
102 static void DCE2_CoSegDecode(DCE2_SsnData *, DCE2_CoTracker *, DCE2_CoSeg *);
103 static void DCE2_CoBind(DCE2_SsnData *, DCE2_CoTracker *,
104 const DceRpcCoHdr *, const uint8_t *, uint16_t);
105 static void DCE2_CoAlterCtx(DCE2_SsnData *, DCE2_CoTracker *,
106 const DceRpcCoHdr *, const uint8_t *, uint16_t);
107 static void DCE2_CoCtxReq(DCE2_SsnData *, DCE2_CoTracker *, const DceRpcCoHdr *,
108 const uint8_t, const uint8_t *, uint16_t);
109 static void DCE2_CoBindAck(DCE2_SsnData *, DCE2_CoTracker *,
110 const DceRpcCoHdr *, const uint8_t *, uint16_t);
111 static void DCE2_CoRequest(DCE2_SsnData *, DCE2_CoTracker *,
112 const DceRpcCoHdr *, const uint8_t *, uint16_t);
113 static void DCE2_CoResponse(DCE2_SsnData *, DCE2_CoTracker *,
114 const DceRpcCoHdr *, const uint8_t *, uint16_t);
115 static void DCE2_CoHandleFrag(DCE2_SsnData *, DCE2_CoTracker *,
116 const DceRpcCoHdr *, const uint8_t *, uint16_t);
117 static inline DCE2_Ret DCE2_CoHandleSegmentation(DCE2_CoSeg *, const uint8_t *,
118 uint16_t, uint16_t, uint16_t *);
119 static void DCE2_CoReassemble(DCE2_SsnData *, DCE2_CoTracker *, DCE2_CoRpktType);
120 static inline void DCE2_CoFragReassemble(DCE2_SsnData *, DCE2_CoTracker *);
121 static DCE2_Ret DCE2_CoSetIface(DCE2_SsnData *, DCE2_CoTracker *, uint16_t);
122 static int DCE2_CoCtxCompare(const void *, const void *);
123 static void DCE2_CoCtxFree(void *);
124
125 static inline void DCE2_CoSetRopts(DCE2_SsnData *, DCE2_CoTracker *, const DceRpcCoHdr *);
126 static inline void DCE2_CoSetRdata(DCE2_SsnData *, DCE2_CoTracker *, uint8_t *, uint16_t);
127 static inline void DCE2_CoResetFragTracker(DCE2_CoFragTracker *);
128 static inline void DCE2_CoResetTracker(DCE2_CoTracker *);
129 static inline DCE2_Ret DCE2_CoInitCtxStorage(DCE2_CoTracker *);
130 static inline void DCE2_CoEraseCtxIds(DCE2_CoTracker *);
131 static inline void DCE2_CoSegAlert(DCE2_SsnData *, DCE2_CoTracker *, DCE2_Event);
132 static inline SFSnortPacket * DCE2_CoGetSegRpkt(DCE2_SsnData *, const uint8_t *, uint32_t);
133 static inline DCE2_RpktType DCE2_CoGetRpktType(DCE2_SsnData *, DCE2_BufType);
134 static SFSnortPacket * DCE2_CoGetRpkt(DCE2_SsnData *, DCE2_CoTracker *, DCE2_CoRpktType, DCE2_RpktType *);
135
136 static inline DCE2_CoSeg * DCE2_CoGetSegPtr(DCE2_SsnData *, DCE2_CoTracker *);
137 static inline DCE2_Buffer * DCE2_CoGetFragBuf(DCE2_SsnData *, DCE2_CoFragTracker *);
138 static inline int DCE2_CoIsSegBuf(DCE2_SsnData *, DCE2_CoTracker *, const uint8_t *);
139 static void DCE2_CoEarlyReassemble(DCE2_SsnData *, DCE2_CoTracker *);
140 static DCE2_Ret DCE2_CoSegEarlyRequest(DCE2_CoTracker *, const uint8_t *, uint32_t);
141 static int DCE2_CoGetAuthLen(DCE2_SsnData *, const DceRpcCoHdr *,
142 const uint8_t *, uint16_t);
143
144 /********************************************************************
145 * Function: DCE2_CoInitRdata()
146 *
147 * Initializes header of defragmentation reassembly packet.
148 * Sets relevant fields in header that will not have to change
149 * from reassembly to reassembly. The reassembly buffer used is
150 * big enough for the header.
151 *
152 * Arguments:
153 * uint8_t *
154 * Pointer to the place in the reassembly packet to set
155 * the header data.
156 *
157 * Returns: None
158 *
159 ********************************************************************/
DCE2_CoInitRdata(uint8_t * co_ptr,int dir)160 void DCE2_CoInitRdata(uint8_t *co_ptr, int dir)
161 {
162 DceRpcCoHdr *co_hdr = (DceRpcCoHdr *)co_ptr;
163
164 /* Set some relevant fields. These should never get reset */
165 co_hdr->pversion.major = DCERPC_PROTO_MAJOR_VERS__5;
166 co_hdr->pfc_flags = (DCERPC_CO_PFC_FLAGS__FIRST_FRAG | DCERPC_CO_PFC_FLAGS__LAST_FRAG);
167 co_hdr->packed_drep[0] = 0x10; /* Little endian */
168
169 if (dir == FLAG_FROM_CLIENT)
170 co_hdr->ptype = DCERPC_PDU_TYPE__REQUEST;
171 else
172 co_hdr->ptype = DCERPC_PDU_TYPE__RESPONSE;
173 }
174
175 /********************************************************************
176 * Function: DCE2_CoSetRdata()
177 *
178 * Sets relevant fields in the defragmentation reassembly packet
179 * based on data gathered from the session and reassembly phase.
180 * The reassembly buffer used is big enough for the headers.
181 *
182 * Arguments:
183 * DCE2_CoTracker *
184 * Pointer to the relevant connection-oriented tracker.
185 * uint8_t *
186 * Pointer to the place in the reassembly packet where the
187 * header starts.
188 * uint16_t
189 * The length of the stub data.
190 *
191 * Returns: None
192 *
193 ********************************************************************/
DCE2_CoSetRdata(DCE2_SsnData * sd,DCE2_CoTracker * cot,uint8_t * co_ptr,uint16_t stub_len)194 static inline void DCE2_CoSetRdata(DCE2_SsnData *sd, DCE2_CoTracker *cot,
195 uint8_t *co_ptr, uint16_t stub_len)
196 {
197 DceRpcCoHdr *co_hdr = (DceRpcCoHdr *)co_ptr;
198 /* If we've set the fragment tracker context id or opnum, use them. */
199 uint16_t ctx_id =
200 (cot->frag_tracker.ctx_id != DCE2_SENTINEL) ?
201 (uint16_t)cot->frag_tracker.ctx_id : (uint16_t)cot->ctx_id;
202 uint16_t opnum =
203 (cot->frag_tracker.opnum != DCE2_SENTINEL) ?
204 (uint16_t)cot->frag_tracker.opnum : (uint16_t)cot->opnum;
205
206 if (DCE2_SsnFromClient(sd->wire_pkt))
207 {
208 DceRpcCoRequest *co_req = (DceRpcCoRequest *)((uint8_t *)co_hdr + sizeof(DceRpcCoHdr));
209 /* Doesn't really matter if this wraps ... it is basically just for presentation */
210 uint16_t flen = sizeof(DceRpcCoHdr) + sizeof(DceRpcCoRequest) + stub_len;
211
212 co_hdr->frag_length = DceRpcHtons(&flen, DCERPC_BO_FLAG__LITTLE_ENDIAN);
213 co_req->context_id = DceRpcHtons(&ctx_id, DCERPC_BO_FLAG__LITTLE_ENDIAN);
214 co_req->opnum = DceRpcHtons(&opnum, DCERPC_BO_FLAG__LITTLE_ENDIAN);
215 }
216 else
217 {
218 DceRpcCoResponse *co_resp = (DceRpcCoResponse *)((uint8_t *)co_hdr + sizeof(DceRpcCoHdr));
219 uint16_t flen = sizeof(DceRpcCoHdr) + sizeof(DceRpcCoResponse) + stub_len;
220
221 co_hdr->frag_length = DceRpcHtons(&flen, DCERPC_BO_FLAG__LITTLE_ENDIAN);
222 co_resp->context_id = DceRpcHtons(&ctx_id, DCERPC_BO_FLAG__LITTLE_ENDIAN);
223 }
224 }
225
226 /********************************************************************
227 * Function: DCE2_CoProcess()
228 *
229 * Main entry point for connection-oriented DCE/RPC processing.
230 * Since there can be more than one DCE/RPC pdu in the packet, it
231 * loops through the packet data until none is left. It handles
232 * transport layer segmentation and buffers data until it gets the
233 * full pdu, then hands off to pdu processing.
234 *
235 * Arguments:
236 * DCE2_SsnData *
237 * Pointer to the session data structure.
238 * DCE2_CoTracker *
239 * Pointer to the relevant connection-oriented tracker.
240 * const uint8_t *
241 * Pointer to packet data
242 * uint16_t
243 * Packet data length
244 *
245 * Returns: None
246 *
247 ********************************************************************/
DCE2_CoProcess(DCE2_SsnData * sd,DCE2_CoTracker * cot,const uint8_t * data_ptr,uint16_t data_len)248 void DCE2_CoProcess(DCE2_SsnData *sd, DCE2_CoTracker *cot,
249 const uint8_t *data_ptr, uint16_t data_len)
250 {
251 DCE2_CoSeg *seg = DCE2_CoGetSegPtr(sd, cot);
252 DCE2_Ret status;
253 uint32_t num_frags = 0;
254
255 dce2_stats.co_pdus++;
256 co_reassembled = 0;
257
258 while (data_len > 0)
259 {
260 num_frags++;
261
262 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "DCE/RPC message number: %u\n", num_frags));
263
264 /* Fast track full fragments */
265 if (DCE2_BufferIsEmpty(seg->buf))
266 {
267 const uint8_t *frag_ptr = data_ptr;
268 uint16_t frag_len;
269 uint16_t data_used;
270
271 /* Not enough data left for a header. Buffer it and return */
272 if (data_len < sizeof(DceRpcCoHdr))
273 {
274 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO,
275 "Not enough data in packet for DCE/RPC Connection-oriented header.\n"));
276
277 DCE2_CoHandleSegmentation(seg, data_ptr, data_len, sizeof(DceRpcCoHdr), &data_used);
278
279 /* Just break out of loop in case early detect is enabled */
280 break;
281 }
282
283 if (DCE2_CoHdrChecks(sd, cot, (DceRpcCoHdr *)data_ptr) != DCE2_RET__SUCCESS)
284 return;
285
286 frag_len = DceRpcCoFragLen((DceRpcCoHdr *)data_ptr);
287
288 /* Not enough data left for the pdu. */
289 if (data_len < frag_len)
290 {
291 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO,
292 "Not enough data in packet for fragment length: %u\n", frag_len));
293
294 /* Set frag length so we don't have to check it again in seg code */
295 seg->frag_len = frag_len;
296
297 DCE2_CoHandleSegmentation(seg, data_ptr, data_len, frag_len, &data_used);
298 break;
299 }
300
301 DCE2_MOVE(data_ptr, data_len, frag_len);
302
303 /* Got a full DCE/RPC pdu */
304 DCE2_CoDecode(sd, cot, frag_ptr, frag_len);
305
306 /* If we're configured to do defragmentation only detect on first frag
307 * since we'll detect on reassembled */
308 if (!DCE2_GcDceDefrag() || ((num_frags == 1) && !co_reassembled))
309 DCE2_Detect(sd);
310
311 /* Reset if this is a last frag */
312 if (DceRpcCoLastFrag((DceRpcCoHdr *)frag_ptr))
313 num_frags = 0;
314 }
315 else /* We've already buffered data */
316 {
317 uint16_t data_used = 0;
318
319 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Segmentation buffer has %u bytes\n",
320 DCE2_BufferLength(seg->buf)));
321
322 /* Need more data to get header */
323 if (DCE2_BufferLength(seg->buf) < sizeof(DceRpcCoHdr))
324 {
325 status = DCE2_CoHandleSegmentation(seg, data_ptr, data_len, sizeof(DceRpcCoHdr), &data_used);
326
327 /* Still not enough for header */
328 if (status != DCE2_RET__SUCCESS)
329 break;
330
331 /* Move the length of the amount of data we used to get header */
332 DCE2_MOVE(data_ptr, data_len, data_used);
333
334 if (DCE2_CoHdrChecks(sd, cot, (DceRpcCoHdr *)DCE2_BufferData(seg->buf)) != DCE2_RET__SUCCESS)
335 {
336 int data_back;
337 DCE2_BufferEmpty(seg->buf);
338 /* Move back to original packet header */
339 data_back = -data_used;
340 DCE2_MOVE(data_ptr, data_len, data_back);
341 /*Check the original packet*/
342 if (DCE2_CoHdrChecks(sd, cot, (DceRpcCoHdr *)data_ptr) != DCE2_RET__SUCCESS)
343 return;
344 else
345 {
346 /*Only use the original packet, ignore the data in seg_buffer*/
347 num_frags = 0;
348 continue;
349 }
350 }
351
352 seg->frag_len = DceRpcCoFragLen((DceRpcCoHdr *)DCE2_BufferData(seg->buf));
353 }
354
355 /* Need more data for full pdu */
356 if (DCE2_BufferLength(seg->buf) < seg->frag_len)
357 {
358 status = DCE2_CoHandleSegmentation(seg, data_ptr, data_len, seg->frag_len, &data_used);
359
360 /* Still not enough */
361 if (status != DCE2_RET__SUCCESS)
362 break;
363
364 DCE2_MOVE(data_ptr, data_len, data_used);
365 }
366
367 /* Do this before calling DCE2_CoSegDecode since it will empty
368 * seg buffer */
369 if (DceRpcCoLastFrag((DceRpcCoHdr *)seg->buf->data))
370 num_frags = 0;
371
372 /* Got the full DCE/RPC pdu. Need to create new packet before decoding */
373 DCE2_CoSegDecode(sd, cot, seg);
374
375 if ( !data_used )
376 break;
377 }
378 }
379
380 if (DCE2_GcReassembleEarly() && !co_reassembled)
381 DCE2_CoEarlyReassemble(sd, cot);
382 }
383
384 /********************************************************************
385 * Function: DCE2_CoHandleSegmentation()
386 *
387 * Wrapper around DCE2_HandleSegmentation() to allocate a new
388 * buffer object if necessary.
389 *
390 * Arguments:
391 * DCE2_CoSeg *
392 * Pointer to a connection-oriented segmentation structure.
393 * uint8_t *
394 * Pointer to the current data cursor in packet.
395 * uint16_t
396 * Length of data from current data cursor.
397 * uint16_t
398 * Length of data that we need in order to consider
399 * desegmentation complete.
400 * uint16_t *
401 * Pointer to basically a return value for the amount of
402 * data in the packet that was actually used for
403 * desegmentation.
404 *
405 * Returns:
406 * DCE2_Ret
407 * DCE2_RET__ERROR if an error occured. Nothing can
408 * be trusted.
409 * DCE2_RET__SEG if there is still more desegmentation
410 * to go, i.e. the need length has not been met by
411 * the data length.
412 * DCE2_RET__SUCCESS if desegmentation is complete,
413 * i.e. the need length was met.
414 *
415 ********************************************************************/
DCE2_CoHandleSegmentation(DCE2_CoSeg * seg,const uint8_t * data_ptr,uint16_t data_len,uint16_t need_len,uint16_t * data_used)416 static inline DCE2_Ret DCE2_CoHandleSegmentation(DCE2_CoSeg *seg,
417 const uint8_t *data_ptr, uint16_t data_len, uint16_t need_len,
418 uint16_t *data_used)
419 {
420 DCE2_Ret status;
421 PROFILE_VARS;
422
423 PREPROC_PROFILE_START(dce2_pstat_co_seg);
424
425 if (seg == NULL)
426 {
427 PREPROC_PROFILE_END(dce2_pstat_co_seg);
428 return DCE2_RET__ERROR;
429 }
430
431 if (seg->buf == NULL)
432 {
433 seg->buf = DCE2_BufferNew(need_len, DCE2_CO__MIN_ALLOC_SIZE, DCE2_MEM_TYPE__CO_SEG);
434 if (seg->buf == NULL)
435 {
436 PREPROC_PROFILE_END(dce2_pstat_co_seg);
437 return DCE2_RET__ERROR;
438 }
439 }
440
441 status = DCE2_HandleSegmentation(seg->buf,
442 data_ptr, data_len, need_len, data_used);
443
444 PREPROC_PROFILE_END(dce2_pstat_co_seg);
445
446 return status;
447 }
448
449 /********************************************************************
450 * Function: DCE2_CoHdrChecks()
451 *
452 * Checks some relevant fields in the header to make sure they're
453 * sane.
454 *
455 * Arguments:
456 * DCE2_SsnData *
457 * Pointer to the session data structure.
458 * DCE2_CoTracker *
459 * Pointer to the relevant connection-oriented tracker.
460 * DceRpcCoHdr *
461 * Pointer to the header struct layed over the packet data.
462 *
463 * Returns:
464 * DCE2_Ret
465 * DCE2_RET__ERROR if we should not continue processing.
466 * DCE2_RET__SUCCESS if we should continue processing.
467 *
468 ********************************************************************/
DCE2_CoHdrChecks(DCE2_SsnData * sd,DCE2_CoTracker * cot,const DceRpcCoHdr * co_hdr)469 static DCE2_Ret DCE2_CoHdrChecks(DCE2_SsnData *sd, DCE2_CoTracker *cot, const DceRpcCoHdr *co_hdr)
470 {
471 uint16_t frag_len = DceRpcCoFragLen(co_hdr);
472 DceRpcPduType pdu_type = DceRpcCoPduType(co_hdr);
473 int is_seg_buf = DCE2_CoIsSegBuf(sd, cot, (uint8_t *)co_hdr);
474
475 if (frag_len < sizeof(DceRpcCoHdr))
476 {
477 /* Assume we autodetected incorrectly or that DCE/RPC is not running
478 * over the SMB named pipe */
479 if (!DCE2_SsnAutodetected(sd) && (sd->trans != DCE2_TRANS_TYPE__SMB))
480 {
481 if (is_seg_buf)
482 DCE2_CoSegAlert(sd, cot, DCE2_EVENT__CO_FLEN_LT_HDR);
483 else
484 DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_HDR, frag_len, sizeof(DceRpcCoHdr));
485 }
486
487 return DCE2_RET__ERROR;
488 }
489
490 if (DceRpcCoVersMaj(co_hdr) != DCERPC_PROTO_MAJOR_VERS__5)
491 {
492 if (!DCE2_SsnAutodetected(sd) && (sd->trans != DCE2_TRANS_TYPE__SMB))
493 {
494 if (is_seg_buf)
495 DCE2_CoSegAlert(sd, cot, DCE2_EVENT__CO_BAD_MAJ_VERSION);
496 else
497 DCE2_Alert(sd, DCE2_EVENT__CO_BAD_MAJ_VERSION, DceRpcCoVersMaj(co_hdr));
498 }
499
500 return DCE2_RET__ERROR;
501 }
502
503 if (DceRpcCoVersMin(co_hdr) != DCERPC_PROTO_MINOR_VERS__0)
504 {
505 if (!DCE2_SsnAutodetected(sd) && (sd->trans != DCE2_TRANS_TYPE__SMB))
506 {
507 if (is_seg_buf)
508 DCE2_CoSegAlert(sd, cot, DCE2_EVENT__CO_BAD_MIN_VERSION);
509 else
510 DCE2_Alert(sd, DCE2_EVENT__CO_BAD_MIN_VERSION, DceRpcCoVersMin(co_hdr));
511 }
512
513 return DCE2_RET__ERROR;
514 }
515
516 if (pdu_type >= DCERPC_PDU_TYPE__MAX)
517 {
518 if (!DCE2_SsnAutodetected(sd) && (sd->trans != DCE2_TRANS_TYPE__SMB))
519 {
520 if (is_seg_buf)
521 DCE2_CoSegAlert(sd, cot, DCE2_EVENT__CO_BAD_PDU_TYPE);
522 else
523 DCE2_Alert(sd, DCE2_EVENT__CO_BAD_PDU_TYPE, DceRpcCoPduType(co_hdr));
524 }
525
526 return DCE2_RET__ERROR;
527 }
528
529 if (DCE2_SsnFromClient(sd->wire_pkt) && (cot->max_xmit_frag != DCE2_SENTINEL))
530 {
531 if (frag_len > cot->max_xmit_frag)
532 {
533 if (is_seg_buf)
534 DCE2_CoSegAlert(sd, cot, DCE2_EVENT__CO_FRAG_GT_MAX_XMIT_FRAG);
535 else
536 DCE2_Alert(sd, DCE2_EVENT__CO_FRAG_GT_MAX_XMIT_FRAG, dce2_pdu_types[pdu_type],
537 frag_len, cot->max_xmit_frag);
538 }
539 else if (!DceRpcCoLastFrag(co_hdr) && (pdu_type == DCERPC_PDU_TYPE__REQUEST)
540 && ((((int)cot->max_xmit_frag - DCE2_MAX_XMIT_SIZE_FUZZ) < 0)
541 || ((int)frag_len < ((int)cot->max_xmit_frag - DCE2_MAX_XMIT_SIZE_FUZZ))))
542 {
543 /* If client needs to fragment the DCE/RPC request, it shouldn't be less than the
544 * maximum xmit size negotiated. Only if it's not a last fragment. Make this alert
545 * only if it is considerably less - have seen legitimate fragments that are just
546 * slightly less the negotiated fragment size. */
547 if (is_seg_buf)
548 DCE2_CoSegAlert(sd, cot, DCE2_EVENT__CO_FRAG_LT_MAX_XMIT_FRAG);
549 else
550 DCE2_Alert(sd, DCE2_EVENT__CO_FRAG_LT_MAX_XMIT_FRAG, dce2_pdu_types[pdu_type],
551 frag_len, cot->max_xmit_frag);
552 }
553
554 /* Continue processing */
555 }
556
557 return DCE2_RET__SUCCESS;
558 }
559
560 /********************************************************************
561 * Function: DCE2_CoDecode()
562 *
563 * Main processing for the DCE/RPC pdu types. Most are not
564 * implemented as, currently, they are not necessary and only
565 * stats are kept for them. Important are the bind, alter context
566 * and request.
567 *
568 * Arguments:
569 * DCE2_SsnData *
570 * Pointer to the session data structure
571 * DCE2_CoTracker *
572 * Pointer to the relevant connection-oriented tracker.
573 * const uint8_t *
574 * Pointer to the start of the DCE/RPC pdu in the packet data.
575 * uint16_t
576 * Fragment length of the pdu.
577 *
578 * Returns: None
579 *
580 ********************************************************************/
DCE2_CoDecode(DCE2_SsnData * sd,DCE2_CoTracker * cot,const uint8_t * frag_ptr,uint16_t frag_len)581 static void DCE2_CoDecode(DCE2_SsnData *sd, DCE2_CoTracker *cot,
582 const uint8_t *frag_ptr, uint16_t frag_len)
583 {
584 /* Already checked that we have enough data for header */
585 const DceRpcCoHdr *co_hdr = (DceRpcCoHdr *)frag_ptr;
586 int pdu_type = DceRpcCoPduType(co_hdr);
587
588 /* We've got the main header. Move past it to the
589 * start of the pdu */
590 DCE2_MOVE(frag_ptr, frag_len, sizeof(DceRpcCoHdr));
591
592 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "PDU type: "));
593
594 /* Client specific pdu types - some overlap with server */
595 if (DCE2_SsnFromClient(sd->wire_pkt))
596 {
597 switch (pdu_type)
598 {
599 case DCERPC_PDU_TYPE__BIND:
600 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Bind\n"));
601 dce2_stats.co_bind++;
602
603 /* Make sure context id list and queue are initialized */
604 if (DCE2_CoInitCtxStorage(cot) != DCE2_RET__SUCCESS)
605 return;
606
607 DCE2_CoBind(sd, cot, co_hdr, frag_ptr, frag_len);
608
609 break;
610
611 case DCERPC_PDU_TYPE__ALTER_CONTEXT:
612 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Alter Context\n"));
613 dce2_stats.co_alter_ctx++;
614
615 if (DCE2_CoInitCtxStorage(cot) != DCE2_RET__SUCCESS)
616 return;
617
618 DCE2_CoAlterCtx(sd, cot, co_hdr, frag_ptr, frag_len);
619
620 break;
621
622 case DCERPC_PDU_TYPE__REQUEST:
623 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Request\n"));
624 dce2_stats.co_request++;
625
626 if (DCE2_ListIsEmpty(cot->ctx_ids) &&
627 DCE2_QueueIsEmpty(cot->pending_ctx_ids))
628 {
629 return;
630 }
631
632 DCE2_CoRequest(sd, cot, co_hdr, frag_ptr, frag_len);
633
634 break;
635
636 case DCERPC_PDU_TYPE__AUTH3:
637 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Auth3\n"));
638 dce2_stats.co_auth3++;
639 break;
640
641 case DCERPC_PDU_TYPE__CO_CANCEL:
642 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Cancel\n"));
643 dce2_stats.co_cancel++;
644 break;
645
646 case DCERPC_PDU_TYPE__ORPHANED:
647 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Orphaned\n"));
648 dce2_stats.co_orphaned++;
649 break;
650
651 case DCERPC_PDU_TYPE__MICROSOFT_PROPRIETARY_OUTLOOK2003_RPC_OVER_HTTP:
652 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Microsoft Request To Send RPC over HTTP\n"));
653 dce2_stats.co_ms_pdu++;
654 break;
655
656 default:
657 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Unknown (0x%02x)\n", pdu_type));
658 dce2_stats.co_other_req++;
659 break;
660 }
661 }
662 else
663 {
664 switch (pdu_type)
665 {
666 case DCERPC_PDU_TYPE__BIND_ACK:
667 case DCERPC_PDU_TYPE__ALTER_CONTEXT_RESP:
668 if (pdu_type == DCERPC_PDU_TYPE__BIND_ACK)
669 {
670 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Bind Ack\n"));
671 dce2_stats.co_bind_ack++;
672 }
673 else
674 {
675 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Alter Context Response\n"));
676 dce2_stats.co_alter_ctx_resp++;
677 }
678
679 if (DCE2_QueueIsEmpty(cot->pending_ctx_ids))
680 return;
681
682 /* Bind ack and alter context response have the same
683 * header structure, just different pdu type */
684 DCE2_CoBindAck(sd, cot, co_hdr, frag_ptr, frag_len);
685
686 /* Got the bind/alter response - clear out the pending queue */
687 DCE2_QueueEmpty(cot->pending_ctx_ids);
688
689 break;
690
691 case DCERPC_PDU_TYPE__BIND_NACK:
692 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Bind Nack\n"));
693 dce2_stats.co_bind_nack++;
694
695 /* Bind nack in Windows seems to blow any previous context away */
696 switch (DCE2_SsnGetServerPolicy(sd))
697 {
698 case DCE2_POLICY__WIN2000:
699 case DCE2_POLICY__WIN2003:
700 case DCE2_POLICY__WINXP:
701 case DCE2_POLICY__WINVISTA:
702 case DCE2_POLICY__WIN2008:
703 case DCE2_POLICY__WIN7:
704 DCE2_CoEraseCtxIds(cot);
705 break;
706
707 default:
708 break;
709 }
710
711 cot->got_bind = 0;
712
713 break;
714
715 case DCERPC_PDU_TYPE__RESPONSE:
716 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Response\n"));
717 dce2_stats.co_response++;
718 DCE2_CoResponse(sd, cot, co_hdr, frag_ptr, frag_len);
719 break;
720
721 case DCERPC_PDU_TYPE__FAULT:
722 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Fault\n"));
723 dce2_stats.co_fault++;
724
725 /* Clear out the client side */
726 DCE2_QueueEmpty(cot->pending_ctx_ids);
727 DCE2_BufferEmpty(cot->cli_seg.buf);
728 DCE2_BufferEmpty(cot->frag_tracker.cli_stub_buf);
729
730 DCE2_CoResetTracker(cot);
731
732 break;
733
734 case DCERPC_PDU_TYPE__SHUTDOWN:
735 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Shutdown\n"));
736 dce2_stats.co_shutdown++;
737 break;
738
739 case DCERPC_PDU_TYPE__REJECT:
740 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Reject\n"));
741 dce2_stats.co_reject++;
742
743 DCE2_QueueEmpty(cot->pending_ctx_ids);
744
745 break;
746
747 case DCERPC_PDU_TYPE__MICROSOFT_PROPRIETARY_OUTLOOK2003_RPC_OVER_HTTP:
748 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Microsoft Request To Send RPC over HTTP\n"));
749 dce2_stats.co_ms_pdu++;
750 break;
751
752 default:
753 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Unknown (0x%02x)\n", pdu_type));
754 dce2_stats.co_other_resp++;
755 break;
756 }
757 }
758 }
759
760 /********************************************************************
761 * Function: DCE2_CoBind()
762 *
763 * Handles the processing of a client bind request. There are
764 * differences between Windows and Samba and even early Samba in
765 * how multiple binds on the session are handled. Processing of
766 * the context id bindings is handed off.
767 *
768 * Arguments:
769 * DCE2_SsnData *
770 * Pointer to the session data structure.
771 * DCE2_CoTracker *
772 * Pointer to the relevant connection-oriented tracker.
773 * DceRpcCoHdr *
774 * Pointer to the main header in the packet data.
775 * const uint8_t *
776 * Pointer to the current processing point of the DCE/RPC
777 * pdu in the packet data.
778 * uint16_t
779 * Fragment length left in the pdu.
780 *
781 * Returns: None
782 *
783 ********************************************************************/
DCE2_CoBind(DCE2_SsnData * sd,DCE2_CoTracker * cot,const DceRpcCoHdr * co_hdr,const uint8_t * frag_ptr,uint16_t frag_len)784 static void DCE2_CoBind(DCE2_SsnData *sd, DCE2_CoTracker *cot,
785 const DceRpcCoHdr *co_hdr, const uint8_t *frag_ptr, uint16_t frag_len)
786 {
787 DCE2_Policy policy = DCE2_SsnGetServerPolicy(sd);
788 DceRpcCoBind *bind = (DceRpcCoBind *)frag_ptr;
789
790 if (frag_len < sizeof(DceRpcCoBind))
791 {
792 DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_SIZE, dce2_pdu_types[DceRpcCoPduType(co_hdr)],
793 frag_len, sizeof(DceRpcCoBind));
794 return;
795 }
796
797 DCE2_MOVE(frag_ptr, frag_len, sizeof(DceRpcCoBind));
798
799 switch (policy)
800 {
801 case DCE2_POLICY__WIN2000:
802 case DCE2_POLICY__WIN2003:
803 case DCE2_POLICY__WINXP:
804 case DCE2_POLICY__WINVISTA:
805 case DCE2_POLICY__WIN2008:
806 case DCE2_POLICY__WIN7:
807 /* Windows will not accept more than one bind */
808 if (!DCE2_ListIsEmpty(cot->ctx_ids))
809 {
810 /* Delete context id list if anything there */
811 DCE2_CoEraseCtxIds(cot);
812 return;
813 }
814
815 /* Byte order of stub data will be that of the bind */
816 cot->data_byte_order = DceRpcCoByteOrder(co_hdr);
817
818 break;
819
820 case DCE2_POLICY__SAMBA:
821 case DCE2_POLICY__SAMBA_3_0_37:
822 case DCE2_POLICY__SAMBA_3_0_22:
823 if (cot->got_bind)
824 return;
825
826 break;
827
828 case DCE2_POLICY__SAMBA_3_0_20:
829 /* Accepts multiple binds */
830 break;
831
832 default:
833 DCE2_Log(DCE2_LOG_TYPE__ERROR,
834 "%s(%d) Invalid policy: %d",
835 __FILE__, __LINE__, policy);
836 return;
837 }
838
839 cot->max_xmit_frag = (int)DceRpcCoBindMaxXmitFrag(co_hdr, bind);
840 DCE2_CoCtxReq(sd, cot, co_hdr, DceRpcCoNumCtxItems(bind), frag_ptr, frag_len);
841 }
842
843 /********************************************************************
844 * Function: DCE2_CoAlterCtx()
845 *
846 * Handles the processing of a client alter context request.
847 * Again, differences in how this is handled - whether we've seen
848 * a bind yet or not, altering the data byte order. Processing
849 * of the context id bindings is handed off.
850 *
851 * Arguments:
852 * DCE2_SsnData *
853 * Pointer to the session data structure.
854 * DCE2_CoTracker *
855 * Pointer to the relevant connection-oriented tracker.
856 * DceRpcCoHdr *
857 * Pointer to the main header in the packet data.
858 * const uint8_t *
859 * Pointer to the current processing point of the DCE/RPC
860 * pdu in the packet data.
861 * uint16_t
862 * Fragment length left in the pdu.
863 *
864 * Returns: None
865 *
866 ********************************************************************/
DCE2_CoAlterCtx(DCE2_SsnData * sd,DCE2_CoTracker * cot,const DceRpcCoHdr * co_hdr,const uint8_t * frag_ptr,uint16_t frag_len)867 static void DCE2_CoAlterCtx(DCE2_SsnData *sd, DCE2_CoTracker *cot,
868 const DceRpcCoHdr *co_hdr, const uint8_t *frag_ptr, uint16_t frag_len)
869 {
870 DCE2_Policy policy = DCE2_SsnGetServerPolicy(sd);
871 DceRpcCoAltCtx *alt_ctx = (DceRpcCoAltCtx *)frag_ptr;
872
873 if (frag_len < sizeof(DceRpcCoAltCtx))
874 {
875 DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_SIZE, dce2_pdu_types[DceRpcCoPduType(co_hdr)],
876 frag_len, sizeof(DceRpcCoAltCtx));
877 return;
878 }
879
880 DCE2_MOVE(frag_ptr, frag_len, sizeof(DceRpcCoAltCtx));
881
882 switch (policy)
883 {
884 case DCE2_POLICY__WIN2000:
885 case DCE2_POLICY__WIN2003:
886 case DCE2_POLICY__WINXP:
887 case DCE2_POLICY__WINVISTA:
888 case DCE2_POLICY__WIN2008:
889 case DCE2_POLICY__WIN7:
890 /* Windows will not accept an alter context before
891 * bind and will bind_nak it */
892 if (DCE2_ListIsEmpty(cot->ctx_ids))
893 return;
894
895 if (cot->data_byte_order != (int)DceRpcCoByteOrder(co_hdr))
896 {
897 /* This is anomalous behavior. Alert, but continue processing */
898 if (cot->data_byte_order != DCE2_SENTINEL)
899 DCE2_Alert(sd, DCE2_EVENT__CO_ALTER_CHANGE_BYTE_ORDER);
900 }
901
902 break;
903
904 case DCE2_POLICY__SAMBA:
905 case DCE2_POLICY__SAMBA_3_0_37:
906 case DCE2_POLICY__SAMBA_3_0_22:
907 case DCE2_POLICY__SAMBA_3_0_20:
908 /* Nothing for Samba */
909 break;
910
911 default:
912 DCE2_Log(DCE2_LOG_TYPE__ERROR,
913 "%s(%d) Invalid policy: %d",
914 __FILE__, __LINE__, policy);
915 break;
916 }
917
918 /* Alter context is typedef'ed as a bind */
919 DCE2_CoCtxReq(sd, cot, co_hdr, DceRpcCoNumCtxItems((DceRpcCoBind *)alt_ctx), frag_ptr, frag_len);
920 }
921
922 /********************************************************************
923 * Function: DCE2_CoCtxReq()
924 *
925 * Handles parsing the context id list out of the packet.
926 * Context ids and associated uuids are stored in a queue and
927 * dequeued upon server response. Server response doesn't
928 * indicate by context id which bindings were accepted or
929 * rejected, but the index or order they were in in the client
930 * bind or alter context, hence the queue.
931 *
932 * Arguments:
933 * DCE2_SsnData *
934 * Pointer to the session data structure.
935 * DCE2_CoTracker *
936 * Pointer to the relevant connection-oriented tracker.
937 * DceRpcCoHdr *
938 * Pointer to the main header in the packet data.
939 * const uint8_t
940 * The number of context items in the bind or alter context.
941 * const uint8_t *
942 * Pointer to the current processing point of the DCE/RPC
943 * pdu in the packet data.
944 * uint16_t
945 * Fragment length left in the pdu.
946 *
947 * Returns: None
948 *
949 ********************************************************************/
DCE2_CoCtxReq(DCE2_SsnData * sd,DCE2_CoTracker * cot,const DceRpcCoHdr * co_hdr,const uint8_t num_ctx_items,const uint8_t * frag_ptr,uint16_t frag_len)950 static void DCE2_CoCtxReq(DCE2_SsnData *sd, DCE2_CoTracker *cot, const DceRpcCoHdr *co_hdr,
951 const uint8_t num_ctx_items, const uint8_t *frag_ptr, uint16_t frag_len)
952 {
953 DCE2_Policy policy = DCE2_SsnGetServerPolicy(sd);
954 unsigned int i;
955 DCE2_Ret status;
956
957 if (num_ctx_items == 0)
958 {
959 DCE2_Alert(sd, DCE2_EVENT__CO_ZERO_CTX_ITEMS, dce2_pdu_types[DceRpcCoPduType(co_hdr)]);
960 return;
961 }
962
963 for (i = 0; i < num_ctx_items; i++)
964 {
965 DceRpcCoContElem *ctx_elem = (DceRpcCoContElem *)frag_ptr;
966 uint16_t ctx_id;
967 uint8_t num_tsyns;
968 const Uuid *iface;
969 uint16_t if_vers_maj;
970 uint16_t if_vers_min;
971 DCE2_CoCtxIdNode *ctx_node;
972 int j;
973 PROFILE_VARS;
974
975 if (frag_len < sizeof(DceRpcCoContElem))
976 {
977 DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_SIZE, dce2_pdu_types[DceRpcCoPduType(co_hdr)],
978 frag_len, sizeof(DceRpcCoContElem));
979 return;
980 }
981
982 ctx_id = DceRpcCoContElemCtxId(co_hdr, ctx_elem);
983 num_tsyns = DceRpcCoContElemNumTransSyntaxes(ctx_elem);
984 iface = DceRpcCoContElemIface(ctx_elem);
985 if_vers_maj = DceRpcCoContElemIfaceVersMaj(co_hdr, ctx_elem);
986 if_vers_min = DceRpcCoContElemIfaceVersMin(co_hdr, ctx_elem);
987
988 /* No transfer syntaxes */
989 if (num_tsyns == 0)
990 {
991 DCE2_Alert(sd, DCE2_EVENT__CO_ZERO_TSYNS, dce2_pdu_types[DceRpcCoPduType(co_hdr)]);
992 return;
993 }
994
995 DCE2_MOVE(frag_ptr, frag_len, sizeof(DceRpcCoContElem));
996
997 /* Don't really care about the transfer syntaxes */
998 for (j = 0; j < num_tsyns; j++)
999 {
1000 if (frag_len < sizeof(DceRpcCoSynId))
1001 {
1002 DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_SIZE, dce2_pdu_types[DceRpcCoPduType(co_hdr)],
1003 frag_len, sizeof(DceRpcCoSynId));
1004 return;
1005 }
1006
1007 DCE2_MOVE(frag_ptr, frag_len, sizeof(DceRpcCoSynId));
1008 }
1009
1010 PREPROC_PROFILE_START(dce2_pstat_co_ctx);
1011
1012 /* If there is already an accepted node with in the list
1013 * with this ctx, just return */
1014 if (policy == DCE2_POLICY__SAMBA_3_0_20)
1015 {
1016 ctx_node = DCE2_ListFind(cot->ctx_ids, (void *)(uintptr_t)ctx_id);
1017 if ((ctx_node != NULL) && (ctx_node->state != DCE2_CO_CTX_STATE__REJECTED))
1018 {
1019 PREPROC_PROFILE_END(dce2_pstat_co_ctx);
1020 return;
1021 }
1022 }
1023
1024 ctx_node = (DCE2_CoCtxIdNode *)DCE2_Alloc(sizeof(DCE2_CoCtxIdNode), DCE2_MEM_TYPE__CO_CTX);
1025 if (ctx_node == NULL)
1026 {
1027 PREPROC_PROFILE_END(dce2_pstat_co_ctx);
1028 return;
1029 }
1030
1031 /* Add context id to pending queue */
1032 status = DCE2_QueueEnqueue(cot->pending_ctx_ids, ctx_node);
1033 if (status != DCE2_RET__SUCCESS)
1034 {
1035 DCE2_Free((void *)ctx_node, sizeof(DCE2_CoCtxIdNode), DCE2_MEM_TYPE__CO_CTX);
1036 PREPROC_PROFILE_END(dce2_pstat_co_ctx);
1037 return;
1038 }
1039
1040 /* This node will get moved to the context id list upon server response */
1041 ctx_node->ctx_id = ctx_id;
1042 DCE2_CopyUuid(&ctx_node->iface, iface, DceRpcCoByteOrder(co_hdr));
1043 ctx_node->iface_vers_maj = if_vers_maj;
1044 ctx_node->iface_vers_min = if_vers_min;
1045 ctx_node->state = DCE2_CO_CTX_STATE__PENDING;
1046
1047 PREPROC_PROFILE_END(dce2_pstat_co_ctx);
1048
1049 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Added Context item to queue.\n"
1050 " Context id: %u\n"
1051 " Interface: %s\n"
1052 " Interface major version: %u\n"
1053 " Interface minor version: %u\n",
1054 ctx_node->ctx_id,
1055 DCE2_UuidToStr(&ctx_node->iface, DCERPC_BO_FLAG__NONE),
1056 ctx_node->iface_vers_maj, ctx_node->iface_vers_min));
1057
1058 switch (policy)
1059 {
1060 case DCE2_POLICY__SAMBA:
1061 case DCE2_POLICY__SAMBA_3_0_37:
1062 case DCE2_POLICY__SAMBA_3_0_22:
1063 case DCE2_POLICY__SAMBA_3_0_20:
1064 /* Samba only ever looks at one context item. Not sure
1065 * if this is an alertable offense */
1066 return;
1067
1068 default:
1069 break;
1070 }
1071 }
1072 }
1073
1074 /********************************************************************
1075 * Function: DCE2_CoBindAck()
1076 *
1077 * Handles the processing of a server bind ack or a server alter
1078 * context response since they share the same header.
1079 * Moves context id items from the pending queue into a list
1080 * ultimately used by the rule options and sets each context item
1081 * as accepted or rejected based on the server response.
1082 *
1083 * Arguments:
1084 * DCE2_SsnData *
1085 * Pointer to the session data structure.
1086 * DCE2_CoTracker *
1087 * Pointer to the relevant connection-oriented tracker.
1088 * DceRpcCoHdr *
1089 * Pointer to the main header in the packet data.
1090 * const uint8_t *
1091 * Pointer to the current processing point of the DCE/RPC
1092 * pdu in the packet data.
1093 * uint16_t
1094 * Fragment length left in the pdu.
1095 *
1096 * Returns: None
1097 *
1098 ********************************************************************/
DCE2_CoBindAck(DCE2_SsnData * sd,DCE2_CoTracker * cot,const DceRpcCoHdr * co_hdr,const uint8_t * frag_ptr,uint16_t frag_len)1099 static void DCE2_CoBindAck(DCE2_SsnData *sd, DCE2_CoTracker *cot,
1100 const DceRpcCoHdr *co_hdr, const uint8_t *frag_ptr, uint16_t frag_len)
1101 {
1102 DCE2_Policy policy = DCE2_SsnGetServerPolicy(sd);
1103 DceRpcCoBindAck *bind_ack = (DceRpcCoBindAck *)frag_ptr;
1104 uint16_t sec_addr_len;
1105 const uint8_t *ctx_data;
1106 uint16_t ctx_len;
1107 uint16_t pad = 0;
1108 DceRpcCoContResultList *ctx_list;
1109 uint8_t num_ctx_results;
1110 unsigned int i;
1111 uint16_t max_recv_frag;
1112 DCE2_Ret status;
1113
1114 if (frag_len < sizeof(DceRpcCoBindAck))
1115 {
1116 DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_SIZE, dce2_pdu_types[DceRpcCoPduType(co_hdr)],
1117 frag_len, sizeof(DceRpcCoBindAck));
1118 return;
1119 }
1120
1121 DCE2_MOVE(frag_ptr, frag_len, sizeof(DceRpcCoBindAck));
1122
1123 /* Set what should be the maximum amount of data a client can send in a fragment */
1124 max_recv_frag = DceRpcCoBindAckMaxRecvFrag(co_hdr, bind_ack);
1125 if ((cot->max_xmit_frag == DCE2_SENTINEL) || (max_recv_frag < cot->max_xmit_frag))
1126 cot->max_xmit_frag = (int)max_recv_frag;
1127
1128 sec_addr_len = DceRpcCoSecAddrLen(co_hdr, bind_ack);
1129
1130 ctx_data = frag_ptr;
1131 ctx_len = frag_len;
1132
1133 /* First move past secondary address */
1134 if (ctx_len < sec_addr_len)
1135 {
1136 DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_SIZE, dce2_pdu_types[DceRpcCoPduType(co_hdr)],
1137 ctx_len, sec_addr_len);
1138 return;
1139 }
1140
1141 DCE2_MOVE(ctx_data, ctx_len, sec_addr_len);
1142
1143 /* padded to 4 octet */
1144 if ((sizeof(DceRpcCoBindAck) + sec_addr_len) & 3)
1145 pad = (4 - ((sizeof(DceRpcCoBindAck) + sec_addr_len) & 3));
1146
1147 if (ctx_len < pad)
1148 {
1149 DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_SIZE, dce2_pdu_types[DceRpcCoPduType(co_hdr)],
1150 ctx_len, pad);
1151 return;
1152 }
1153
1154 DCE2_MOVE(ctx_data, ctx_len, pad);
1155
1156 /* Now we're at the start of the context item results */
1157 if (ctx_len < sizeof(DceRpcCoContResultList))
1158 {
1159 DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_SIZE, dce2_pdu_types[DceRpcCoPduType(co_hdr)],
1160 ctx_len, sizeof(DceRpcCoContResultList));
1161 return;
1162 }
1163
1164 ctx_list = (DceRpcCoContResultList *)ctx_data;
1165 num_ctx_results = DceRpcCoContNumResults(ctx_list);
1166
1167 DCE2_MOVE(ctx_data, ctx_len, sizeof(DceRpcCoContResultList));
1168
1169 for (i = 0; i < num_ctx_results; i++)
1170 {
1171 DceRpcCoContResult *ctx_result;
1172 uint16_t result;
1173 DCE2_CoCtxIdNode *ctx_node, *existing_ctx_node;
1174 PROFILE_VARS;
1175
1176 if (ctx_len < sizeof(DceRpcCoContResult))
1177 {
1178 DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_SIZE, dce2_pdu_types[DceRpcCoPduType(co_hdr)],
1179 ctx_len, sizeof(DceRpcCoContResult));
1180 return;
1181 }
1182
1183 ctx_result = (DceRpcCoContResult *)ctx_data;
1184 result = DceRpcCoContRes(co_hdr, ctx_result);
1185
1186 DCE2_MOVE(ctx_data, ctx_len, sizeof(DceRpcCoContResult));
1187
1188 if (DCE2_QueueIsEmpty(cot->pending_ctx_ids))
1189 return;
1190
1191 PREPROC_PROFILE_START(dce2_pstat_co_ctx);
1192
1193 /* Dequeue context item in pending queue - this will get put in the permanent
1194 * context id list or free'd */
1195 ctx_node = (DCE2_CoCtxIdNode *)DCE2_QueueDequeue(cot->pending_ctx_ids);
1196 if (ctx_node == NULL)
1197 {
1198 DCE2_Log(DCE2_LOG_TYPE__ERROR,
1199 "%s(%d) Failed to dequeue a context id node.",
1200 __FILE__, __LINE__);
1201 PREPROC_PROFILE_END(dce2_pstat_co_ctx);
1202 return;
1203 }
1204
1205 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Adding Context item to context item list.\n"
1206 " Context id: %u\n"
1207 " Interface: %s\n"
1208 " Interface major version: %u\n"
1209 " Interface minor version: %u\n",
1210 ctx_node->ctx_id,
1211 DCE2_UuidToStr(&ctx_node->iface, DCERPC_BO_FLAG__NONE),
1212 ctx_node->iface_vers_maj, ctx_node->iface_vers_min));
1213
1214 if (result == DCERPC_CO_CONT_DEF_RESULT__ACCEPTANCE)
1215 {
1216 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Server accepted context item.\n"));
1217 ctx_node->state = DCE2_CO_CTX_STATE__ACCEPTED;
1218 if (DceRpcCoPduType(co_hdr) == DCERPC_PDU_TYPE__BIND_ACK)
1219 cot->got_bind = 1;
1220 }
1221 else
1222 {
1223 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Server rejected context item.\n"));
1224 ctx_node->state = DCE2_CO_CTX_STATE__REJECTED;
1225 cot->got_bind = 0;
1226 }
1227
1228 existing_ctx_node =
1229 (DCE2_CoCtxIdNode *)DCE2_ListFind(cot->ctx_ids, (void *)(uintptr_t)ctx_node->ctx_id);
1230
1231 if (existing_ctx_node != NULL)
1232 {
1233 switch (policy)
1234 {
1235 case DCE2_POLICY__WIN2000:
1236 case DCE2_POLICY__WIN2003:
1237 case DCE2_POLICY__WINXP:
1238 case DCE2_POLICY__WINVISTA:
1239 case DCE2_POLICY__WIN2008:
1240 case DCE2_POLICY__WIN7:
1241 if (ctx_node->state == DCE2_CO_CTX_STATE__REJECTED)
1242 break;
1243
1244 if (existing_ctx_node->state == DCE2_CO_CTX_STATE__REJECTED)
1245 {
1246 existing_ctx_node->ctx_id = ctx_node->ctx_id;
1247 DCE2_CopyUuid(&existing_ctx_node->iface, &ctx_node->iface, DCERPC_BO_FLAG__NONE);
1248 existing_ctx_node->iface_vers_maj = ctx_node->iface_vers_maj;
1249 existing_ctx_node->iface_vers_min = ctx_node->iface_vers_min;
1250 existing_ctx_node->state = ctx_node->state;
1251 }
1252
1253 break;
1254
1255 case DCE2_POLICY__SAMBA:
1256 case DCE2_POLICY__SAMBA_3_0_37:
1257 case DCE2_POLICY__SAMBA_3_0_22:
1258 case DCE2_POLICY__SAMBA_3_0_20:
1259 /* Samba actually alters the context. Windows keeps the old */
1260 if (ctx_node->state != DCE2_CO_CTX_STATE__REJECTED)
1261 {
1262 existing_ctx_node->ctx_id = ctx_node->ctx_id;
1263 DCE2_CopyUuid(&existing_ctx_node->iface, &ctx_node->iface, DCERPC_BO_FLAG__NONE);
1264 existing_ctx_node->iface_vers_maj = ctx_node->iface_vers_maj;
1265 existing_ctx_node->iface_vers_min = ctx_node->iface_vers_min;
1266 existing_ctx_node->state = ctx_node->state;
1267 }
1268
1269 break;
1270
1271 default:
1272 break;
1273 }
1274
1275 DCE2_Free((void *)ctx_node, sizeof(DCE2_CoCtxIdNode), DCE2_MEM_TYPE__CO_CTX);
1276 }
1277 else
1278 {
1279 status = DCE2_ListInsert(cot->ctx_ids, (void *)(uintptr_t)ctx_node->ctx_id, (void *)ctx_node);
1280 if (status != DCE2_RET__SUCCESS)
1281 {
1282 /* Hit memcap */
1283 DCE2_Free((void *)ctx_node, sizeof(DCE2_CoCtxIdNode), DCE2_MEM_TYPE__CO_CTX);
1284 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO,
1285 "Failed to add context id node to list.\n"));
1286 PREPROC_PROFILE_END(dce2_pstat_co_ctx);
1287 return;
1288 }
1289 }
1290
1291 PREPROC_PROFILE_END(dce2_pstat_co_ctx);
1292 }
1293 }
1294
1295 /********************************************************************
1296 * Function: DCE2_CoRequest()
1297 *
1298 * Handles a DCE/RPC request from the client. This is were the
1299 * client actually asks the server to do stuff on it's behalf.
1300 * If it's a first/last fragment, set relevant rule option
1301 * data and return. If it's a true fragment, do some target
1302 * based futzing to set the right opnum and context id for
1303 * the to be reassembled packet.
1304 *
1305 * Arguments:
1306 * DCE2_SsnData *
1307 * Pointer to the session data structure.
1308 * DCE2_CoTracker *
1309 * Pointer to the relevant connection-oriented tracker.
1310 * DceRpcCoHdr *
1311 * Pointer to the main header in the packet data.
1312 * const uint8_t *
1313 * Pointer to the current processing point of the DCE/RPC
1314 * pdu in the packet data.
1315 * uint16_t
1316 * Fragment length left in the pdu.
1317 *
1318 * Returns: None
1319 *
1320 ********************************************************************/
DCE2_CoRequest(DCE2_SsnData * sd,DCE2_CoTracker * cot,const DceRpcCoHdr * co_hdr,const uint8_t * frag_ptr,uint16_t frag_len)1321 static void DCE2_CoRequest(DCE2_SsnData *sd, DCE2_CoTracker *cot,
1322 const DceRpcCoHdr *co_hdr, const uint8_t *frag_ptr, uint16_t frag_len)
1323 {
1324 DceRpcCoRequest *rhdr = (DceRpcCoRequest *)frag_ptr;
1325 uint16_t req_size = sizeof(DceRpcCoRequest);
1326 DCE2_Policy policy = DCE2_SsnGetServerPolicy(sd);
1327
1328 /* Account for possible object uuid */
1329 if (DceRpcCoObjectFlag(co_hdr))
1330 req_size += sizeof(Uuid);
1331
1332 if (frag_len < req_size)
1333 {
1334 DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_SIZE, dce2_pdu_types[DceRpcCoPduType(co_hdr)],
1335 frag_len, req_size);
1336 return;
1337 }
1338
1339 switch (policy)
1340 {
1341 /* After 3.0.37 up to 3.5.2 byte order of stub data is always
1342 * interpreted as little endian */
1343 case DCE2_POLICY__SAMBA:
1344 cot->data_byte_order = DCERPC_BO_FLAG__LITTLE_ENDIAN;
1345 break;
1346
1347 case DCE2_POLICY__SAMBA_3_0_37:
1348 case DCE2_POLICY__SAMBA_3_0_22:
1349 case DCE2_POLICY__SAMBA_3_0_20:
1350 cot->data_byte_order = DceRpcCoByteOrder(co_hdr);
1351 break;
1352
1353 default:
1354 break;
1355 }
1356
1357 /* Move past header */
1358 DCE2_MOVE(frag_ptr, frag_len, req_size);
1359
1360 /* If for some reason we had some fragments queued */
1361 if (DceRpcCoFirstFrag(co_hdr) && !DceRpcCoLastFrag(co_hdr)
1362 && !DCE2_BufferIsEmpty(cot->frag_tracker.cli_stub_buf))
1363 {
1364 DCE2_CoFragReassemble(sd, cot);
1365 DCE2_BufferEmpty(cot->frag_tracker.cli_stub_buf);
1366 DCE2_CoResetFragTracker(&cot->frag_tracker);
1367 }
1368
1369 cot->stub_data = frag_ptr;
1370 cot->opnum = DceRpcCoOpnum(co_hdr, rhdr);
1371 cot->ctx_id = DceRpcCoCtxId(co_hdr, rhdr);
1372 cot->call_id = DceRpcCoCallId(co_hdr);
1373
1374 if (DceRpcCoFirstFrag(co_hdr) && DceRpcCoLastFrag(co_hdr))
1375 {
1376 int auth_len = DCE2_CoGetAuthLen(sd, co_hdr, frag_ptr, frag_len);
1377 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "First and last fragment.\n"));
1378 if (auth_len == -1)
1379 return;
1380 DCE2_CoSetRopts(sd, cot, co_hdr);
1381 }
1382 else
1383 {
1384 DCE2_CoFragTracker *ft = &cot->frag_tracker;
1385 int auth_len = DCE2_CoGetAuthLen(sd, co_hdr, frag_ptr, frag_len);
1386
1387 dce2_stats.co_req_fragments++;
1388
1389 #ifdef DEBUG_MSGS
1390 DCE2_DEBUG_CODE(DCE2_DEBUG__CO,
1391 if (DceRpcCoFirstFrag(co_hdr)) DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "First fragment.\n"));
1392 else if (DceRpcCoLastFrag(co_hdr)) DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Last fragment.\n"));
1393 else DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Middle fragment.\n"));
1394 DCE2_PrintPktData(frag_ptr, frag_len););
1395 #endif
1396
1397 if (auth_len == -1)
1398 return;
1399
1400 if (DCE2_BufferIsEmpty(ft->cli_stub_buf))
1401 {
1402 ft->expected_opnum = cot->opnum;
1403 ft->expected_ctx_id = cot->ctx_id;
1404 ft->expected_call_id = cot->call_id;
1405 }
1406 else
1407 {
1408 /* Don't return for these, because we can still process and servers
1409 * will still accept and deal with the anomalies in their own way */
1410 if ((ft->expected_opnum != DCE2_SENTINEL) &&
1411 (ft->expected_opnum != cot->opnum))
1412 {
1413 DCE2_Alert(sd, DCE2_EVENT__CO_FRAG_DIFF_OPNUM,
1414 cot->opnum, ft->expected_opnum);
1415 }
1416
1417 if ((ft->expected_ctx_id != DCE2_SENTINEL) &&
1418 (ft->expected_ctx_id != cot->ctx_id))
1419 {
1420 DCE2_Alert(sd, DCE2_EVENT__CO_FRAG_DIFF_CTX_ID,
1421 cot->ctx_id, ft->expected_ctx_id);
1422 }
1423
1424 if ((ft->expected_call_id != DCE2_SENTINEL) &&
1425 (ft->expected_call_id != cot->call_id))
1426 {
1427 DCE2_Alert(sd, DCE2_EVENT__CO_FRAG_DIFF_CALL_ID,
1428 cot->call_id, ft->expected_call_id);
1429 }
1430 }
1431
1432 /* Possibly set opnum in frag tracker */
1433 switch (policy)
1434 {
1435 case DCE2_POLICY__WIN2000:
1436 case DCE2_POLICY__WIN2003:
1437 case DCE2_POLICY__WINXP:
1438 case DCE2_POLICY__SAMBA:
1439 case DCE2_POLICY__SAMBA_3_0_37:
1440 case DCE2_POLICY__SAMBA_3_0_22:
1441 case DCE2_POLICY__SAMBA_3_0_20:
1442 if (DceRpcCoLastFrag(co_hdr))
1443 ft->opnum = cot->opnum;
1444 break;
1445
1446 case DCE2_POLICY__WINVISTA:
1447 case DCE2_POLICY__WIN2008:
1448 case DCE2_POLICY__WIN7:
1449 if (DceRpcCoFirstFrag(co_hdr))
1450 ft->opnum = cot->opnum;
1451 break;
1452
1453 default:
1454 DCE2_Log(DCE2_LOG_TYPE__ERROR,
1455 "%s(%d) Invalid policy: %d",
1456 __FILE__, __LINE__, policy);
1457 break;
1458 }
1459
1460 /* Possibly set context id in frag tracker */
1461 switch (policy)
1462 {
1463 case DCE2_POLICY__WIN2000:
1464 case DCE2_POLICY__WIN2003:
1465 case DCE2_POLICY__WINXP:
1466 case DCE2_POLICY__WINVISTA:
1467 case DCE2_POLICY__WIN2008:
1468 case DCE2_POLICY__WIN7:
1469 if (DceRpcCoFirstFrag(co_hdr))
1470 {
1471 ft->ctx_id = cot->ctx_id;
1472 }
1473 else if ((ft->expected_call_id != DCE2_SENTINEL) &&
1474 (ft->expected_call_id != cot->call_id))
1475 {
1476 /* Server won't accept frag */
1477 return;
1478 }
1479
1480 break;
1481
1482 case DCE2_POLICY__SAMBA:
1483 case DCE2_POLICY__SAMBA_3_0_37:
1484 case DCE2_POLICY__SAMBA_3_0_22:
1485 case DCE2_POLICY__SAMBA_3_0_20:
1486 if (DceRpcCoLastFrag(co_hdr))
1487 {
1488 ft->ctx_id = cot->ctx_id;
1489 }
1490
1491 break;
1492
1493 default:
1494 DCE2_Log(DCE2_LOG_TYPE__ERROR,
1495 "%s(%d) Invalid policy: %d",
1496 __FILE__, __LINE__, policy);
1497 break;
1498 }
1499
1500 DCE2_CoSetRopts(sd, cot, co_hdr);
1501
1502 /* If we're configured to do defragmentation */
1503 if (DCE2_GcDceDefrag())
1504 {
1505 /* Don't want to include authentication data in fragment */
1506 DCE2_CoHandleFrag(sd, cot, co_hdr, frag_ptr,
1507 (uint16_t)(frag_len - (uint16_t)auth_len));
1508 }
1509 }
1510 }
1511
1512 /********************************************************************
1513 * Function: DCE2_CoResponse()
1514 *
1515 * Handles a DCE/RPC response from the server.
1516 * Samba responds to SMB bind write, request write before read with
1517 * a response to the request and doesn't send a bind ack. Get the
1518 * context id from the pending context id list and put in stable
1519 * list.
1520 *
1521 * Arguments:
1522 * DCE2_SsnData *
1523 * Pointer to the session data structure.
1524 * DCE2_CoTracker *
1525 * Pointer to the relevant connection-oriented tracker.
1526 * DceRpcCoHdr *
1527 * Pointer to the main header in the packet data.
1528 * const uint8_t *
1529 * Pointer to the current processing point of the DCE/RPC
1530 * pdu in the packet data.
1531 * uint16_t
1532 * Fragment length left in the pdu.
1533 *
1534 * Returns: None
1535 *
1536 ********************************************************************/
DCE2_CoResponse(DCE2_SsnData * sd,DCE2_CoTracker * cot,const DceRpcCoHdr * co_hdr,const uint8_t * frag_ptr,uint16_t frag_len)1537 static void DCE2_CoResponse(DCE2_SsnData *sd, DCE2_CoTracker *cot,
1538 const DceRpcCoHdr *co_hdr, const uint8_t *frag_ptr, uint16_t frag_len)
1539 {
1540 DceRpcCoResponse *rhdr = (DceRpcCoResponse *)frag_ptr;
1541 uint16_t ctx_id;
1542 DCE2_Policy policy = DCE2_SsnGetServerPolicy(sd);
1543
1544 if (frag_len < sizeof(DceRpcCoResponse))
1545 {
1546 DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_SIZE, dce2_pdu_types[DceRpcCoPduType(co_hdr)],
1547 frag_len, sizeof(DceRpcCoResponse));
1548 return;
1549 }
1550
1551 switch (policy)
1552 {
1553 case DCE2_POLICY__SAMBA:
1554 cot->data_byte_order = DCERPC_BO_FLAG__LITTLE_ENDIAN;
1555 break;
1556
1557 case DCE2_POLICY__SAMBA_3_0_37:
1558 case DCE2_POLICY__SAMBA_3_0_22:
1559 case DCE2_POLICY__SAMBA_3_0_20:
1560 cot->data_byte_order = DceRpcCoByteOrder(co_hdr);
1561 break;
1562
1563 default:
1564 break;
1565 }
1566
1567 ctx_id = DceRpcCoCtxIdResp(co_hdr, rhdr);
1568
1569 /* If pending queue is not empty, add this context id as accepted and all
1570 * others as pending */
1571 while (!DCE2_QueueIsEmpty(cot->pending_ctx_ids))
1572 {
1573 DCE2_Ret status;
1574 DCE2_CoCtxIdNode *ctx_node = (DCE2_CoCtxIdNode *)DCE2_QueueDequeue(cot->pending_ctx_ids);
1575
1576 if (ctx_node == NULL)
1577 {
1578 DCE2_Log(DCE2_LOG_TYPE__ERROR,
1579 "%s(%d) Failed to dequeue a context id node.",
1580 __FILE__, __LINE__);
1581 return;
1582 }
1583
1584 if (ctx_node->ctx_id == ctx_id)
1585 ctx_node->state = DCE2_CO_CTX_STATE__ACCEPTED;
1586
1587 status = DCE2_ListInsert(cot->ctx_ids, (void *)(uintptr_t)ctx_node->ctx_id, (void *)ctx_node);
1588 if (status != DCE2_RET__SUCCESS)
1589 {
1590 /* Might be a duplicate in there already. If there is we would have used it
1591 * anyway before looking at the pending queue. Just get rid of it */
1592 DCE2_Free((void *)ctx_node, sizeof(DCE2_CoCtxIdNode), DCE2_MEM_TYPE__CO_CTX);
1593 return;
1594 }
1595 }
1596
1597 /* Move past header */
1598 DCE2_MOVE(frag_ptr, frag_len, sizeof(DceRpcCoResponse));
1599
1600 /* If for some reason we had some fragments queued */
1601 if (DceRpcCoFirstFrag(co_hdr) && !DCE2_BufferIsEmpty(cot->frag_tracker.srv_stub_buf))
1602 {
1603 DCE2_CoFragReassemble(sd, cot);
1604 DCE2_BufferEmpty(cot->frag_tracker.srv_stub_buf);
1605 DCE2_CoResetFragTracker(&cot->frag_tracker);
1606 }
1607
1608 cot->stub_data = frag_ptr;
1609 /* Opnum not in response header - have to use previous client's */
1610 cot->ctx_id = ctx_id;
1611 cot->call_id = DceRpcCoCallId(co_hdr);
1612
1613 if (DceRpcCoFirstFrag(co_hdr) && DceRpcCoLastFrag(co_hdr))
1614 {
1615 int auth_len = DCE2_CoGetAuthLen(sd, co_hdr, frag_ptr, frag_len);
1616 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "First and last fragment.\n"));
1617 if (auth_len == -1)
1618 return;
1619 DCE2_CoSetRopts(sd, cot, co_hdr);
1620 }
1621 else
1622 {
1623 //DCE2_CoFragTracker *ft = &cot->frag_tracker;
1624 int auth_len = DCE2_CoGetAuthLen(sd, co_hdr, frag_ptr, frag_len);
1625
1626 dce2_stats.co_resp_fragments++;
1627 if (auth_len == -1)
1628 return;
1629
1630 #if 0
1631 /* TBD - Target based foo */
1632 /* Don't return for these, because we can still process */
1633 if ((ft->expected_opnum != DCE2_SENTINEL) &&
1634 (ft->expected_opnum != cot->opnum))
1635 {
1636 DCE2_Alert(sd, DCE2_EVENT__CO_FRAG_DIFF_OPNUM,
1637 cot->opnum, ft->expected_opnum);
1638 }
1639
1640 if ((ft->expected_ctx_id != DCE2_SENTINEL) &&
1641 (ft->expected_ctx_id != cot->ctx_id))
1642 {
1643 DCE2_Alert(sd, DCE2_EVENT__CO_FRAG_DIFF_CTX_ID,
1644 cot->ctx_id, ft->expected_ctx_id);
1645 }
1646
1647 if ((ft->expected_call_id != DCE2_SENTINEL) &&
1648 (ft->expected_call_id != cot->call_id))
1649 {
1650 DCE2_Alert(sd, DCE2_EVENT__CO_FRAG_DIFF_CALL_ID,
1651 cot->call_id, ft->expected_call_id);
1652 }
1653 #endif
1654
1655 DCE2_CoSetRopts(sd, cot, co_hdr);
1656
1657 /* If we're configured to do defragmentation */
1658 if (DCE2_GcDceDefrag())
1659 {
1660 DCE2_CoHandleFrag(sd, cot, co_hdr, frag_ptr,
1661 (uint16_t)(frag_len - (uint16_t)auth_len));
1662 }
1663 }
1664 }
1665
1666 /********************************************************************
1667 * Function: DCE2_CoHandleFrag()
1668 *
1669 * Handles adding a fragment to the defragmentation buffer.
1670 * Does overflow checking. Maximum length of fragmentation buffer
1671 * is based on the maximum packet length Snort can handle.
1672 *
1673 * Arguments:
1674 * DCE2_SsnData *
1675 * Pointer to the session data structure.
1676 * DCE2_CoTracker *
1677 * Pointer to the relevant connection-oriented tracker.
1678 * DceRpcCoHdr *
1679 * Pointer to the main header in the packet data.
1680 * const uint8_t *
1681 * Pointer to the current processing point of the DCE/RPC
1682 * pdu in the packet data.
1683 * uint16_t
1684 * Fragment length left in the pdu.
1685 *
1686 * Returns: None
1687 *
1688 ********************************************************************/
DCE2_CoHandleFrag(DCE2_SsnData * sd,DCE2_CoTracker * cot,const DceRpcCoHdr * co_hdr,const uint8_t * frag_ptr,uint16_t frag_len)1689 static void DCE2_CoHandleFrag(DCE2_SsnData *sd, DCE2_CoTracker *cot,
1690 const DceRpcCoHdr *co_hdr, const uint8_t *frag_ptr, uint16_t frag_len)
1691 {
1692 DCE2_Buffer *frag_buf = DCE2_CoGetFragBuf(sd, &cot->frag_tracker);
1693 uint32_t size = (frag_len < DCE2_CO__MIN_ALLOC_SIZE) ? DCE2_CO__MIN_ALLOC_SIZE : frag_len;
1694 uint16_t max_frag_data;
1695 DCE2_BufferMinAddFlag mflag = DCE2_BUFFER_MIN_ADD_FLAG__USE;
1696 DCE2_Ret status;
1697 PROFILE_VARS;
1698
1699 PREPROC_PROFILE_START(dce2_pstat_co_frag);
1700
1701 if (DCE2_SsnFromClient(sd->wire_pkt))
1702 {
1703 if (frag_len > dce2_stats.co_cli_max_frag_size)
1704 dce2_stats.co_cli_max_frag_size = frag_len;
1705
1706 if (dce2_stats.co_cli_min_frag_size == 0 || frag_len < dce2_stats.co_cli_min_frag_size)
1707 dce2_stats.co_cli_min_frag_size = frag_len;
1708 }
1709 else
1710 {
1711 if (frag_len > dce2_stats.co_srv_max_frag_size)
1712 dce2_stats.co_srv_max_frag_size = frag_len;
1713
1714 if (dce2_stats.co_srv_min_frag_size == 0 || frag_len < dce2_stats.co_srv_min_frag_size)
1715 dce2_stats.co_srv_min_frag_size = frag_len;
1716 }
1717
1718 if (frag_buf == NULL)
1719 {
1720 if (DCE2_SsnFromServer(sd->wire_pkt))
1721 {
1722 cot->frag_tracker.srv_stub_buf =
1723 DCE2_BufferNew(size, DCE2_CO__MIN_ALLOC_SIZE, DCE2_MEM_TYPE__CO_FRAG);
1724 frag_buf = cot->frag_tracker.srv_stub_buf;
1725 }
1726 else
1727 {
1728 cot->frag_tracker.cli_stub_buf =
1729 DCE2_BufferNew(size, DCE2_CO__MIN_ALLOC_SIZE, DCE2_MEM_TYPE__CO_FRAG);
1730 frag_buf = cot->frag_tracker.cli_stub_buf;
1731 }
1732
1733 if (frag_buf == NULL)
1734 {
1735 PREPROC_PROFILE_START(dce2_pstat_co_frag);
1736 return;
1737 }
1738 }
1739
1740 /* If there's already data in the buffer and this is a first frag
1741 * we probably missed packets */
1742 if (DceRpcCoFirstFrag(co_hdr) && !DCE2_BufferIsEmpty(frag_buf))
1743 {
1744 DCE2_CoResetFragTracker(&cot->frag_tracker);
1745 DCE2_BufferEmpty(frag_buf);
1746 }
1747
1748 /* Check for potential overflow */
1749 if (sd->trans == DCE2_TRANS_TYPE__SMB)
1750 max_frag_data = DCE2_GetRpktMaxData(sd, DCE2_RPKT_TYPE__SMB_CO_FRAG);
1751 else
1752 max_frag_data = DCE2_GetRpktMaxData(sd, DCE2_RPKT_TYPE__TCP_CO_FRAG);
1753
1754 if (DCE2_GcMaxFrag() && (frag_len > DCE2_GcMaxFragLen()))
1755 frag_len = DCE2_GcMaxFragLen();
1756
1757 if ((DCE2_BufferLength(frag_buf) + frag_len) > max_frag_data)
1758 frag_len = max_frag_data - (uint16_t)DCE2_BufferLength(frag_buf);
1759
1760 if (frag_len != 0)
1761 {
1762 /* If it's the last fragment we're going to flush so just alloc
1763 * exactly what we need ... or if there is more data than can fit
1764 * in the reassembly buffer */
1765 if (DceRpcCoLastFrag(co_hdr) || (DCE2_BufferLength(frag_buf) == max_frag_data))
1766 mflag = DCE2_BUFFER_MIN_ADD_FLAG__IGNORE;
1767
1768 status = DCE2_BufferAddData(frag_buf, frag_ptr,
1769 frag_len, DCE2_BufferLength(frag_buf), mflag);
1770
1771 if (status != DCE2_RET__SUCCESS)
1772 {
1773 PREPROC_PROFILE_END(dce2_pstat_co_frag);
1774
1775 /* Either hit memcap or a memcpy failed - reassemble */
1776 DCE2_CoFragReassemble(sd, cot);
1777 DCE2_BufferEmpty(frag_buf);
1778 return;
1779 }
1780 }
1781
1782 PREPROC_PROFILE_END(dce2_pstat_co_frag);
1783
1784 /* Reassemble if we got a last frag ... */
1785 if (DceRpcCoLastFrag(co_hdr))
1786 {
1787 DCE2_CoFragReassemble(sd, cot);
1788 DCE2_BufferEmpty(frag_buf);
1789
1790 /* Set this for the server response since response doesn't
1791 * contain client opnum used */
1792 cot->opnum = cot->frag_tracker.opnum;
1793 DCE2_CoResetFragTracker(&cot->frag_tracker);
1794
1795 /* Return early - rule opts will be set in reassembly handler */
1796 return;
1797 }
1798 else if (DCE2_BufferLength(frag_buf) == max_frag_data)
1799 {
1800 /* ... or can't fit any more data in the buffer
1801 * Don't reset frag tracker */
1802 DCE2_CoFragReassemble(sd, cot);
1803 DCE2_BufferEmpty(frag_buf);
1804 return;
1805 }
1806 }
1807
1808 /********************************************************************
1809 * Function: DCE2_CoFragReassemble()
1810 *
1811 * Wrapper for the generic reassembly function. Calls generic
1812 * reassembly function specifying that we want to do fragmentation
1813 * reassembly.
1814 *
1815 * Arguments:
1816 * DCE2_SsnData *
1817 * Pointer to the session data structure.
1818 * DCE2_CoTracker *
1819 * Pointer to the relevant connection-oriented tracker.
1820 *
1821 * Returns: None
1822 *
1823 ********************************************************************/
DCE2_CoFragReassemble(DCE2_SsnData * sd,DCE2_CoTracker * cot)1824 static inline void DCE2_CoFragReassemble(DCE2_SsnData *sd, DCE2_CoTracker *cot)
1825 {
1826 DCE2_CoReassemble(sd, cot, DCE2_CO_RPKT_TYPE__FRAG);
1827 }
1828
1829 /********************************************************************
1830 * Function: DCE2_CoReassemble()
1831 *
1832 * Gets a reassemly packet based on the transport and the type of
1833 * reassembly we want to do. Sets rule options and calls detect
1834 * on the reassembled packet.
1835 *
1836 * Arguments:
1837 * DCE2_SsnData *
1838 * Pointer to the session data structure.
1839 * DCE2_CoTracker *
1840 * Pointer to the relevant connection-oriented tracker.
1841 * DCE2_CoRpktType
1842 * Specifies whether we want to do segmenation, fragmentation
1843 * or fragmentation and segmentation reassembly.
1844 *
1845 * Returns: None
1846 *
1847 ********************************************************************/
DCE2_CoReassemble(DCE2_SsnData * sd,DCE2_CoTracker * cot,DCE2_CoRpktType co_rtype)1848 static void DCE2_CoReassemble(DCE2_SsnData *sd, DCE2_CoTracker *cot, DCE2_CoRpktType co_rtype)
1849 {
1850 DCE2_RpktType rpkt_type;
1851 DceRpcCoHdr *co_hdr;
1852 SFSnortPacket *rpkt;
1853 int smb_hdr_len = DCE2_SsnFromClient(sd->wire_pkt) ? DCE2_MOCK_HDR_LEN__SMB_CLI : DCE2_MOCK_HDR_LEN__SMB_SRV;
1854 int co_hdr_len = DCE2_SsnFromClient(sd->wire_pkt) ? DCE2_MOCK_HDR_LEN__CO_CLI : DCE2_MOCK_HDR_LEN__CO_SRV;
1855 PROFILE_VARS;
1856
1857 PREPROC_PROFILE_START(dce2_pstat_co_reass);
1858
1859 rpkt = DCE2_CoGetRpkt(sd, cot, co_rtype, &rpkt_type);
1860 if (rpkt == NULL)
1861 {
1862 DCE2_Log(DCE2_LOG_TYPE__ERROR,
1863 "%s(%d) Could not create DCE/RPC frag reassembled packet.\n",
1864 __FILE__, __LINE__);
1865 PREPROC_PROFILE_END(dce2_pstat_co_reass);
1866 return;
1867 }
1868
1869 switch (rpkt_type)
1870 {
1871 case DCE2_RPKT_TYPE__SMB_CO_FRAG:
1872 case DCE2_RPKT_TYPE__SMB_CO_SEG:
1873 DCE2_SmbSetRdata((DCE2_SmbSsnData *)sd, (uint8_t *)rpkt->payload,
1874 (uint16_t)(rpkt->payload_size - smb_hdr_len));
1875
1876 if (rpkt_type == DCE2_RPKT_TYPE__SMB_CO_FRAG)
1877 {
1878 DCE2_CoSetRdata(sd, cot, (uint8_t *)rpkt->payload + smb_hdr_len,
1879 (uint16_t)(rpkt->payload_size - (smb_hdr_len + co_hdr_len)));
1880
1881 if (DCE2_SsnFromClient(sd->wire_pkt))
1882 dce2_stats.co_cli_frag_reassembled++;
1883 else
1884 dce2_stats.co_srv_frag_reassembled++;
1885 }
1886 else
1887 {
1888 if (DCE2_SsnFromClient(sd->wire_pkt))
1889 dce2_stats.co_cli_seg_reassembled++;
1890 else
1891 dce2_stats.co_srv_seg_reassembled++;
1892 }
1893
1894 co_hdr = (DceRpcCoHdr *)(rpkt->payload + smb_hdr_len);
1895 cot->stub_data = rpkt->payload + smb_hdr_len + co_hdr_len;
1896
1897 break;
1898
1899 case DCE2_RPKT_TYPE__TCP_CO_FRAG:
1900 case DCE2_RPKT_TYPE__TCP_CO_SEG:
1901 if (rpkt_type == DCE2_RPKT_TYPE__TCP_CO_FRAG)
1902 {
1903 DCE2_CoSetRdata(sd, cot, (uint8_t *)rpkt->payload, (uint16_t)(rpkt->payload_size - co_hdr_len));
1904
1905 if (DCE2_SsnFromClient(sd->wire_pkt))
1906 dce2_stats.co_cli_frag_reassembled++;
1907 else
1908 dce2_stats.co_srv_frag_reassembled++;
1909 }
1910 else
1911 {
1912 if (DCE2_SsnFromClient(sd->wire_pkt))
1913 dce2_stats.co_cli_seg_reassembled++;
1914 else
1915 dce2_stats.co_srv_seg_reassembled++;
1916 }
1917
1918 co_hdr = (DceRpcCoHdr *)rpkt->payload;
1919 cot->stub_data = rpkt->payload + co_hdr_len;
1920
1921 break;
1922
1923 default:
1924 DCE2_Log(DCE2_LOG_TYPE__ERROR,
1925 "%s(%d) Invalid rpkt type: %d",
1926 __FILE__, __LINE__, rpkt_type);
1927 PREPROC_PROFILE_END(dce2_pstat_co_reass);
1928 return;
1929 }
1930
1931 PREPROC_PROFILE_END(dce2_pstat_co_reass);
1932
1933 /* Push packet onto stack */
1934 if (DCE2_PushPkt(rpkt) != DCE2_RET__SUCCESS)
1935 {
1936 DCE2_Log(DCE2_LOG_TYPE__ERROR,
1937 "%s(%d) Failed to push packet onto packet stack.",
1938 __FILE__, __LINE__);
1939 return;
1940 }
1941
1942 DCE2_CoSetRopts(sd, cot, co_hdr);
1943
1944 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Reassembled CO fragmented packet:\n"));
1945 DCE2_DEBUG_CODE(DCE2_DEBUG__CO, DCE2_PrintPktData(rpkt->payload, rpkt->payload_size););
1946
1947 DCE2_Detect(sd);
1948 DCE2_PopPkt();
1949
1950 co_reassembled = 1;
1951 }
1952
1953 /********************************************************************
1954 * Function: DCE2_CoSetIface()
1955 *
1956 * Sets the interface UUID for the rules options. Looks in the
1957 * context id list. If nothing found there, it looks in the pending
1958 * list (in case we never saw the server response because of
1959 * missed packets) to see if something is there.
1960 *
1961 * Arguments:
1962 * DCE2_SsnData *
1963 * Pointer to the session data structure.
1964 * DCE2_CoTracker *
1965 * Pointer to the relevant connection-oriented tracker.
1966 * uint16_t
1967 * The context id to use for the lookup.
1968 *
1969 * Returns:
1970 * DCE2_Ret
1971 * DCE2_RET__ERROR if the interface UUID could not be found
1972 * based on the context id passed in.
1973 * DCE2_RET__SUCESS if the interface UUID could be found and
1974 * the appropriate rule options could be set.
1975 *
1976 ********************************************************************/
DCE2_CoSetIface(DCE2_SsnData * sd,DCE2_CoTracker * cot,uint16_t ctx_id)1977 static DCE2_Ret DCE2_CoSetIface(DCE2_SsnData *sd, DCE2_CoTracker *cot, uint16_t ctx_id)
1978 {
1979 DCE2_CoCtxIdNode *ctx_id_node;
1980 PROFILE_VARS;
1981
1982 /* This should be set if we've gotten a Bind */
1983 if (cot->ctx_ids == NULL)
1984 return DCE2_RET__ERROR;
1985
1986 PREPROC_PROFILE_START(dce2_pstat_co_ctx);
1987
1988 ctx_id_node = (DCE2_CoCtxIdNode *)DCE2_ListFind(cot->ctx_ids, (void *)(uintptr_t)ctx_id);
1989 if (ctx_id_node == NULL) /* context id not found in list */
1990 {
1991 /* See if it's in the queue. An easy evasion would be to stagger the writes
1992 * and reads such that we see a request before seeing the server bind ack */
1993 if (cot->pending_ctx_ids != NULL)
1994 {
1995 for (ctx_id_node = (DCE2_CoCtxIdNode *)DCE2_QueueFirst(cot->pending_ctx_ids);
1996 ctx_id_node != NULL;
1997 ctx_id_node = (DCE2_CoCtxIdNode *)DCE2_QueueNext(cot->pending_ctx_ids))
1998 {
1999 if (ctx_id_node->ctx_id == ctx_id)
2000 break;
2001 }
2002 }
2003
2004 if (ctx_id_node == NULL)
2005 {
2006 PREPROC_PROFILE_END(dce2_pstat_co_ctx);
2007 return DCE2_RET__ERROR;
2008 }
2009 }
2010
2011 if (ctx_id_node->state == DCE2_CO_CTX_STATE__REJECTED)
2012 {
2013 PREPROC_PROFILE_END(dce2_pstat_co_ctx);
2014 return DCE2_RET__ERROR;
2015 }
2016
2017 DCE2_CopyUuid(&sd->ropts.iface, &ctx_id_node->iface, DCERPC_BO_FLAG__NONE);
2018 sd->ropts.iface_vers_maj = ctx_id_node->iface_vers_maj;
2019 sd->ropts.iface_vers_min = ctx_id_node->iface_vers_min;
2020
2021 PREPROC_PROFILE_END(dce2_pstat_co_ctx);
2022
2023 return DCE2_RET__SUCCESS;
2024 }
2025
2026 /********************************************************************
2027 * Function: DCE2_CoCtxCompare()
2028 *
2029 * Callback to context id list for finding the right interface
2030 * UUID node. Values passed in are context ids which are used as
2031 * the keys for the list.
2032 *
2033 * Arguments:
2034 * const void *
2035 * First context id to compare.
2036 * const void *
2037 * Second context id to compare.
2038 *
2039 * Returns:
2040 * int
2041 * 0 if first value equals second value
2042 * -1 if first value does not equal the second value
2043 *
2044 ********************************************************************/
DCE2_CoCtxCompare(const void * a,const void * b)2045 static int DCE2_CoCtxCompare(const void *a, const void *b)
2046 {
2047 int x = (int)(uintptr_t)a;
2048 int y = (int)(uintptr_t)b;
2049
2050 if (x == y)
2051 return 0;
2052
2053 /* Only care about equality for finding */
2054 return -1;
2055 }
2056
2057 /********************************************************************
2058 * Function: DCE2_CoCtxFree()
2059 *
2060 * Callback to context id list for freeing context id nodes in
2061 * the list.
2062 *
2063 * Arguments:
2064 * void *
2065 * Context id node to free.
2066 *
2067 * Returns: None
2068 *
2069 ********************************************************************/
DCE2_CoCtxFree(void * data)2070 static void DCE2_CoCtxFree(void *data)
2071 {
2072 if (data == NULL)
2073 return;
2074
2075 DCE2_Free(data, sizeof(DCE2_CoCtxIdNode), DCE2_MEM_TYPE__CO_CTX);
2076 }
2077
2078 /********************************************************************
2079 * Function: DCE2_CoInitTracker()
2080 *
2081 * Initializes fields in the connection-oriented tracker to
2082 * sentinels. Many decisions are made based on whether or not
2083 * these fields have been set.
2084 *
2085 * Arguments:
2086 * DCE2_CoTracker *
2087 * Pointer to the relevant connection-oriented tracker.
2088 *
2089 * Returns: None
2090 *
2091 ********************************************************************/
DCE2_CoInitTracker(DCE2_CoTracker * cot)2092 void DCE2_CoInitTracker(DCE2_CoTracker *cot)
2093 {
2094 if (cot == NULL)
2095 return;
2096
2097 cot->max_xmit_frag = DCE2_SENTINEL;
2098 cot->data_byte_order = DCE2_SENTINEL;
2099 cot->ctx_id = DCE2_SENTINEL;
2100 cot->opnum = DCE2_SENTINEL;
2101 cot->call_id = DCE2_SENTINEL;
2102 cot->stub_data = NULL;
2103 cot->got_bind = 0;
2104
2105 cot->frag_tracker.opnum = DCE2_SENTINEL;
2106 cot->frag_tracker.ctx_id = DCE2_SENTINEL;
2107 cot->frag_tracker.expected_call_id = DCE2_SENTINEL;
2108 cot->frag_tracker.expected_opnum = DCE2_SENTINEL;
2109 cot->frag_tracker.expected_ctx_id = DCE2_SENTINEL;
2110 }
2111
2112 /********************************************************************
2113 * Function: DCE2_CoResetTracker()
2114 *
2115 * Resets frag tracker fields after having reassembled.
2116 *
2117 * Arguments:
2118 * DCE2_CoFragTracker *
2119 * Pointer to the relevant connection-oriented frag tracker.
2120 *
2121 * Returns: None
2122 *
2123 ********************************************************************/
DCE2_CoResetFragTracker(DCE2_CoFragTracker * ft)2124 static inline void DCE2_CoResetFragTracker(DCE2_CoFragTracker *ft)
2125 {
2126 if (ft == NULL)
2127 return;
2128
2129 ft->opnum = DCE2_SENTINEL;
2130 ft->ctx_id = DCE2_SENTINEL;
2131 ft->expected_call_id = DCE2_SENTINEL;
2132 ft->expected_ctx_id = DCE2_SENTINEL;
2133 ft->expected_opnum = DCE2_SENTINEL;
2134 }
2135
2136 /********************************************************************
2137 * Function: DCE2_CoResetTracker()
2138 *
2139 * Resets fields that are transient for requests after the bind or
2140 * alter context. The context id and opnum are dependent on the
2141 * request and in the case of fragmented requests are set until all
2142 * fragments are received. If we got a full request or all of the
2143 * fragments, these should be reset.
2144 *
2145 * Arguments:
2146 * DCE2_CoTracker *
2147 * Pointer to the relevant connection-oriented tracker.
2148 *
2149 * Returns: None
2150 *
2151 ********************************************************************/
DCE2_CoResetTracker(DCE2_CoTracker * cot)2152 static inline void DCE2_CoResetTracker(DCE2_CoTracker *cot)
2153 {
2154 if (cot == NULL)
2155 return;
2156
2157 cot->ctx_id = DCE2_SENTINEL;
2158 cot->opnum = DCE2_SENTINEL;
2159 cot->call_id = DCE2_SENTINEL;
2160 cot->stub_data = NULL;
2161
2162 DCE2_CoResetFragTracker(&cot->frag_tracker);
2163 }
2164
2165 /********************************************************************
2166 * Function: DCE2_CoCleanTracker()
2167 *
2168 * Destroys all dynamically allocated data associated with
2169 * connection-oriented tracker.
2170 *
2171 * Arguments:
2172 * DCE2_CoTracker *
2173 * Pointer to the relevant connection-oriented tracker.
2174 *
2175 * Returns: None
2176 *
2177 ********************************************************************/
DCE2_CoCleanTracker(DCE2_CoTracker * cot)2178 void DCE2_CoCleanTracker(DCE2_CoTracker *cot)
2179 {
2180 if (cot == NULL)
2181 return;
2182
2183 DCE2_BufferDestroy(cot->frag_tracker.cli_stub_buf);
2184 cot->frag_tracker.cli_stub_buf = NULL;
2185
2186 DCE2_BufferDestroy(cot->frag_tracker.srv_stub_buf);
2187 cot->frag_tracker.srv_stub_buf = NULL;
2188
2189 DCE2_BufferDestroy(cot->cli_seg.buf);
2190 cot->cli_seg.buf = NULL;
2191
2192 DCE2_BufferDestroy(cot->srv_seg.buf);
2193 cot->srv_seg.buf = NULL;
2194
2195 DCE2_ListDestroy(cot->ctx_ids);
2196 cot->ctx_ids = NULL;
2197
2198 DCE2_QueueDestroy(cot->pending_ctx_ids);
2199 cot->pending_ctx_ids = NULL;
2200
2201 DCE2_CoInitTracker(cot);
2202 }
2203
2204 /********************************************************************
2205 * Function: DCE2_CoEraseCtxIds()
2206 *
2207 * Empties out the context id list and the pending context id
2208 * queue. Does not free the list and queue - might need to still
2209 * use them.
2210 *
2211 * Arguments:
2212 * DCE2_CoTracker *
2213 * Pointer to the relevant connection-oriented tracker.
2214 *
2215 * Returns: None
2216 *
2217 ********************************************************************/
DCE2_CoEraseCtxIds(DCE2_CoTracker * cot)2218 static inline void DCE2_CoEraseCtxIds(DCE2_CoTracker *cot)
2219 {
2220 if (cot == NULL)
2221 return;
2222
2223 DCE2_QueueEmpty(cot->pending_ctx_ids);
2224 DCE2_ListEmpty(cot->ctx_ids);
2225 }
2226
2227 /********************************************************************
2228 * Function: DCE2_CoSegAlert()
2229 *
2230 * We have to alert with the appropriate data so the alert actually
2231 * makes sense. In this case, the alert was generated based on
2232 * data in a segmentation buffer. We have to create a packet
2233 * using the segmentation buffer before actually alerting.
2234 *
2235 * Arguments:
2236 * DCE2_SsnData *
2237 * Pointer to the session data structure.
2238 * DCE2_CoTracker *
2239 * Pointer to the relevant connection-oriented tracker.
2240 * DCE2_Event
2241 * The event that was generated.
2242 *
2243 * Returns: None
2244 *
2245 ********************************************************************/
DCE2_CoSegAlert(DCE2_SsnData * sd,DCE2_CoTracker * cot,DCE2_Event event)2246 static inline void DCE2_CoSegAlert(DCE2_SsnData *sd, DCE2_CoTracker *cot, DCE2_Event event)
2247 {
2248 SFSnortPacket *rpkt;
2249 DCE2_Buffer *buf;
2250 DceRpcCoHdr *co_hdr;
2251 uint16_t frag_len;
2252 DceRpcPduType pdu_type;
2253
2254 if (DCE2_SsnFromClient(sd->wire_pkt))
2255 buf = cot->cli_seg.buf;
2256 else
2257 buf = cot->srv_seg.buf;
2258
2259 /* This should be called from the desegmentation code after there is
2260 * enough data for a connection oriented header. All of the alerts
2261 * here require a header. */
2262 if (DCE2_BufferIsEmpty(buf) ||
2263 (DCE2_BufferLength(buf) < sizeof(DceRpcCoHdr)))
2264 {
2265 return;
2266 }
2267
2268 rpkt = DCE2_CoGetSegRpkt(sd, DCE2_BufferData(buf), DCE2_BufferLength(buf));
2269 if (rpkt == NULL)
2270 return;
2271
2272 co_hdr = (DceRpcCoHdr *)DCE2_BufferData(buf);
2273 frag_len = DceRpcCoFragLen(co_hdr);
2274 pdu_type = DceRpcCoPduType(co_hdr);
2275
2276 if (DCE2_PushPkt(rpkt) != DCE2_RET__SUCCESS)
2277 {
2278 DCE2_Log(DCE2_LOG_TYPE__ERROR,
2279 "%s(%d) Failed to push packet onto packet stack.",
2280 __FILE__, __LINE__);
2281 return;
2282 }
2283
2284 switch (event)
2285 {
2286 case DCE2_EVENT__CO_FLEN_LT_HDR:
2287 DCE2_Alert(sd, event, frag_len, sizeof(DceRpcCoHdr));
2288 break;
2289
2290 case DCE2_EVENT__CO_BAD_MAJ_VERSION:
2291 DCE2_Alert(sd, event, DceRpcCoVersMaj(co_hdr));
2292 break;
2293
2294 case DCE2_EVENT__CO_BAD_MIN_VERSION:
2295 DCE2_Alert(sd, event, DceRpcCoVersMin(co_hdr));
2296 break;
2297
2298 case DCE2_EVENT__CO_BAD_PDU_TYPE:
2299 DCE2_Alert(sd, event, DceRpcCoPduType(co_hdr));
2300 break;
2301
2302 case DCE2_EVENT__CO_FRAG_GT_MAX_XMIT_FRAG:
2303 DCE2_Alert(sd, event, dce2_pdu_types[pdu_type],
2304 frag_len, cot->max_xmit_frag);
2305 break;
2306
2307 case DCE2_EVENT__CO_FRAG_LT_MAX_XMIT_FRAG:
2308 DCE2_Alert(sd, event, dce2_pdu_types[pdu_type],
2309 frag_len, cot->max_xmit_frag);
2310 break;
2311
2312 default:
2313 break;
2314 }
2315
2316 DCE2_PopPkt();
2317 }
2318
2319 /********************************************************************
2320 * Function: DCE2_CoGetSegRpkt()
2321 *
2322 * Gets and returns a reassembly packet based on a segmentation
2323 * buffer.
2324 *
2325 * Arguments:
2326 * DCE2_SsnData *
2327 * Pointer to the session data structure.
2328 * const uint8_t *
2329 * Pointer to the start of data in the segmentation buffer.
2330 * uint32_t
2331 * The length of the data in the segmentation buffer.
2332 *
2333 * Returns:
2334 * SFSnortPacket *
2335 * A valid pointer to a reassembled packet on success.
2336 * NULL on error.
2337 *
2338 ********************************************************************/
DCE2_CoGetSegRpkt(DCE2_SsnData * sd,const uint8_t * data_ptr,uint32_t data_len)2339 static inline SFSnortPacket * DCE2_CoGetSegRpkt(DCE2_SsnData *sd,
2340 const uint8_t *data_ptr, uint32_t data_len)
2341 {
2342 SFSnortPacket *rpkt = NULL;
2343 int smb_hdr_len = DCE2_SsnFromClient(sd->wire_pkt) ? DCE2_MOCK_HDR_LEN__SMB_CLI : DCE2_MOCK_HDR_LEN__SMB_SRV;
2344
2345 switch (sd->trans)
2346 {
2347 case DCE2_TRANS_TYPE__SMB:
2348 rpkt = DCE2_GetRpkt(sd->wire_pkt, DCE2_RPKT_TYPE__SMB_CO_SEG,
2349 data_ptr, data_len);
2350 if (rpkt == NULL)
2351 {
2352 DCE2_Log(DCE2_LOG_TYPE__ERROR,
2353 "%s(%d) Failed to create reassembly packet.",
2354 __FILE__, __LINE__);
2355 return NULL;
2356 }
2357
2358 DCE2_SmbSetRdata((DCE2_SmbSsnData *)sd, (uint8_t *)rpkt->payload,
2359 (uint16_t)(rpkt->payload_size - smb_hdr_len));
2360
2361 break;
2362
2363 case DCE2_TRANS_TYPE__TCP:
2364 case DCE2_TRANS_TYPE__HTTP_PROXY:
2365 case DCE2_TRANS_TYPE__HTTP_SERVER:
2366 rpkt = DCE2_GetRpkt(sd->wire_pkt, DCE2_RPKT_TYPE__TCP_CO_SEG,
2367 data_ptr, data_len);
2368 if (rpkt == NULL)
2369 {
2370 DCE2_Log(DCE2_LOG_TYPE__ERROR,
2371 "%s(%d) Failed to create reassembly packet.",
2372 __FILE__, __LINE__);
2373 return NULL;
2374 }
2375
2376 break;
2377
2378 default:
2379 DCE2_Log(DCE2_LOG_TYPE__ERROR,
2380 "%s(%d) Invalid transport type: %d",
2381 __FILE__, __LINE__, sd->trans);
2382 break;
2383 }
2384
2385 return rpkt;
2386 }
2387
2388 /********************************************************************
2389 * Function: DCE2_CoInitCtxStorage()
2390 *
2391 * Allocates, if necessary, and initializes the context id list
2392 * and the context id pending queue.
2393 *
2394 * Arguments:
2395 * DCE2_CoTracker *
2396 * Pointer to the relevant connection-oriented tracker.
2397 *
2398 * Returns:
2399 * DCE2_Ret
2400 * DCE2_RET__ERROR
2401 * We were not able to allocate data for new lists.
2402 * DCE2_RET__SUCCESS
2403 * We were able to allocate and initialize new lists.
2404 *
2405 ********************************************************************/
DCE2_CoInitCtxStorage(DCE2_CoTracker * cot)2406 static inline DCE2_Ret DCE2_CoInitCtxStorage(DCE2_CoTracker *cot)
2407 {
2408 if (cot == NULL)
2409 return DCE2_RET__ERROR;
2410
2411 if (cot->ctx_ids == NULL)
2412 {
2413 cot->ctx_ids = DCE2_ListNew(DCE2_LIST_TYPE__SPLAYED, DCE2_CoCtxCompare, DCE2_CoCtxFree,
2414 NULL, DCE2_LIST_FLAG__NO_DUPS, DCE2_MEM_TYPE__CO_CTX);
2415 if (cot->ctx_ids == NULL)
2416 return DCE2_RET__ERROR;
2417 }
2418
2419 if (cot->pending_ctx_ids == NULL)
2420 {
2421 cot->pending_ctx_ids = DCE2_QueueNew(DCE2_CoCtxFree, DCE2_MEM_TYPE__CO_CTX);
2422 if (cot->pending_ctx_ids == NULL)
2423 {
2424 DCE2_ListDestroy(cot->ctx_ids);
2425 cot->ctx_ids = NULL;
2426 return DCE2_RET__ERROR;
2427 }
2428 }
2429 else if (!DCE2_QueueIsEmpty(cot->pending_ctx_ids))
2430 {
2431 DCE2_QueueEmpty(cot->pending_ctx_ids);
2432 }
2433
2434 return DCE2_RET__SUCCESS;
2435 }
2436
2437 /********************************************************************
2438 * Function: DCE2_CoEarlyReassemble()
2439 *
2440 * Checks to see if we should send a reassembly packet based on
2441 * the current data in fragmentation and segmentation buffers
2442 * to the detection engine. Whether we do or not is based on
2443 * whether or not we are configured to do so. The number of bytes
2444 * in the fragmentation and segmentation buffers are calulated
2445 * and if they exceed the amount we are configured for, we
2446 * reassemble.
2447 *
2448 * Arguments:
2449 * DCE2_SsnData *
2450 * Pointer to the session data structure.
2451 * DCE2_CoTracker *
2452 * Pointer to the relevant connection-oriented tracker.
2453 *
2454 * Returns: None
2455 *
2456 ********************************************************************/
DCE2_CoEarlyReassemble(DCE2_SsnData * sd,DCE2_CoTracker * cot)2457 static void DCE2_CoEarlyReassemble(DCE2_SsnData *sd, DCE2_CoTracker *cot)
2458 {
2459 DCE2_Buffer *frag_buf = DCE2_CoGetFragBuf(sd, &cot->frag_tracker);
2460
2461 if (DCE2_SsnFromServer(sd->wire_pkt))
2462 return;
2463
2464 if (!DCE2_BufferIsEmpty(frag_buf))
2465 {
2466 uint32_t bytes = DCE2_BufferLength(frag_buf);
2467 uint32_t seg_bytes = 0;
2468
2469 if (!DCE2_BufferIsEmpty(cot->cli_seg.buf))
2470 {
2471 uint16_t hdr_size = sizeof(DceRpcCoHdr) + sizeof(DceRpcCoRequest);
2472
2473 if (DCE2_BufferLength(cot->cli_seg.buf) > hdr_size)
2474 {
2475 DceRpcCoHdr *co_hdr = (DceRpcCoHdr *)DCE2_BufferData(cot->cli_seg.buf);
2476
2477 if (DceRpcCoPduType(co_hdr) == DCERPC_PDU_TYPE__REQUEST)
2478 {
2479 seg_bytes = DCE2_BufferLength(cot->cli_seg.buf) - hdr_size;
2480
2481 if ((UINT32_MAX - bytes) < seg_bytes)
2482 seg_bytes = UINT32_MAX - bytes;
2483
2484 bytes += seg_bytes;
2485 }
2486 }
2487 }
2488
2489 if (bytes >= DCE2_GcReassembleThreshold())
2490 {
2491 if (seg_bytes == 0)
2492 {
2493 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Early reassemble - DCE/RPC fragments\n"));
2494 DCE2_CoReassemble(sd, cot, DCE2_CO_RPKT_TYPE__FRAG);
2495 }
2496 else
2497 {
2498 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Early reassemble - DCE/RPC fragments and segments\n"));
2499 DCE2_CoReassemble(sd, cot, DCE2_CO_RPKT_TYPE__ALL);
2500 }
2501 }
2502 }
2503 else if (!DCE2_BufferIsEmpty(cot->cli_seg.buf))
2504 {
2505 uint16_t hdr_size = sizeof(DceRpcCoHdr) + sizeof(DceRpcCoRequest);
2506 uint32_t bytes = DCE2_BufferLength(cot->cli_seg.buf);
2507
2508 if (bytes < hdr_size)
2509 return;
2510
2511 if (bytes >= DCE2_GcReassembleThreshold())
2512 {
2513 DCE2_Ret status;
2514
2515 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Early reassemble - DCE/RPC segments\n"));
2516
2517 status = DCE2_CoSegEarlyRequest(cot, DCE2_BufferData(cot->cli_seg.buf), bytes);
2518 if (status != DCE2_RET__SUCCESS)
2519 {
2520 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO,
2521 "Not enough data in seg buffer to set rule option data.\n"));
2522 return;
2523 }
2524
2525 DCE2_CoReassemble(sd, cot, DCE2_CO_RPKT_TYPE__SEG);
2526 }
2527 }
2528 }
2529
2530 /********************************************************************
2531 * Function: DCE2_CoGetRpkt()
2532 *
2533 * Creates a reassembled packet based on the kind of data
2534 * (fragment, segment or both) we want to put in the reassembled
2535 * packet.
2536 *
2537 * Arguments:
2538 * DCE2_SsnData *
2539 * Pointer to the session data structure.
2540 * DCE2_CoTracker *
2541 * Pointer to the relevant connection-oriented tracker.
2542 * DCE2_CoRpktType
2543 * Whether we want a defrag, deseg or defrag + deseg
2544 * reassembled packet.
2545 * DCE2_RpktType *
2546 * This is set based on the transport for the session, and
2547 * potentially used by the caller to set fields in the
2548 * reassembled packet.
2549 *
2550 * Returns:
2551 * SFSnortPacket *
2552 * Pointer to the reassembled packet.
2553 *
2554 ********************************************************************/
DCE2_CoGetRpkt(DCE2_SsnData * sd,DCE2_CoTracker * cot,DCE2_CoRpktType co_rtype,DCE2_RpktType * rtype)2555 static SFSnortPacket * DCE2_CoGetRpkt(DCE2_SsnData *sd, DCE2_CoTracker *cot,
2556 DCE2_CoRpktType co_rtype, DCE2_RpktType *rtype)
2557 {
2558 DCE2_CoSeg *seg_buf = DCE2_CoGetSegPtr(sd, cot);
2559 DCE2_Buffer *frag_buf = DCE2_CoGetFragBuf(sd, &cot->frag_tracker);
2560 const uint8_t *frag_data = NULL, *seg_data = NULL;
2561 uint32_t frag_len = 0, seg_len = 0;
2562 SFSnortPacket *rpkt = NULL;
2563
2564 *rtype = DCE2_RPKT_TYPE__NULL;
2565
2566 switch (co_rtype)
2567 {
2568 case DCE2_CO_RPKT_TYPE__ALL:
2569 if (!DCE2_BufferIsEmpty(frag_buf))
2570 {
2571 frag_data = DCE2_BufferData(frag_buf);
2572 frag_len = DCE2_BufferLength(frag_buf);
2573 }
2574
2575 if (!DCE2_BufferIsEmpty(seg_buf->buf))
2576 {
2577 seg_data = DCE2_BufferData(seg_buf->buf);
2578 seg_len = DCE2_BufferLength(seg_buf->buf);
2579 }
2580
2581 break;
2582
2583 case DCE2_CO_RPKT_TYPE__FRAG:
2584 if (!DCE2_BufferIsEmpty(frag_buf))
2585 {
2586 frag_data = DCE2_BufferData(frag_buf);
2587 frag_len = DCE2_BufferLength(frag_buf);
2588 }
2589
2590 break;
2591
2592 case DCE2_CO_RPKT_TYPE__SEG:
2593 if (!DCE2_BufferIsEmpty(seg_buf->buf))
2594 {
2595 seg_data = DCE2_BufferData(seg_buf->buf);
2596 seg_len = DCE2_BufferLength(seg_buf->buf);
2597 }
2598
2599 break;
2600
2601 default:
2602 DCE2_Log(DCE2_LOG_TYPE__ERROR,
2603 "%s(%d) Invalid CO rpkt type: %d",
2604 __FILE__, __LINE__, co_rtype);
2605 return NULL;
2606 }
2607
2608 /* Seg stub data will be added to end of frag data */
2609 if ((frag_data != NULL) && (seg_data != NULL))
2610 {
2611 uint16_t hdr_size = sizeof(DceRpcCoHdr) + sizeof(DceRpcCoRequest);
2612
2613 /* Need to just extract the stub data from the seg buffer
2614 * if there is enough data there */
2615 if (seg_len > hdr_size)
2616 {
2617 DceRpcCoHdr *co_hdr = (DceRpcCoHdr *)seg_data;
2618
2619 /* Don't use it if it's not a request and therefore doesn't
2620 * belong with the frag data. This is an insanity check -
2621 * shouldn't have seg data that's not a request if there are
2622 * frags queued up */
2623 if (DceRpcCoPduType(co_hdr) != DCERPC_PDU_TYPE__REQUEST)
2624 {
2625 seg_data = NULL;
2626 seg_len = 0;
2627 }
2628 else
2629 {
2630 DCE2_MOVE(seg_data, seg_len, hdr_size);
2631 }
2632 }
2633 else /* Not enough stub data in seg buffer */
2634 {
2635 seg_data = NULL;
2636 seg_len = 0;
2637 }
2638 }
2639
2640 if (frag_data != NULL)
2641 *rtype = DCE2_CoGetRpktType(sd, DCE2_BUF_TYPE__FRAG);
2642 else if (seg_data != NULL)
2643 *rtype = DCE2_CoGetRpktType(sd, DCE2_BUF_TYPE__SEG);
2644
2645 if (*rtype == DCE2_RPKT_TYPE__NULL)
2646 return NULL;
2647
2648 if (frag_data != NULL)
2649 {
2650 rpkt = DCE2_GetRpkt(sd->wire_pkt, *rtype, frag_data, frag_len);
2651 if (rpkt == NULL)
2652 {
2653 DCE2_Log(DCE2_LOG_TYPE__ERROR,
2654 "%s(%d) Failed to create reassembly packet.",
2655 __FILE__, __LINE__);
2656 return NULL;
2657 }
2658
2659 if (seg_data != NULL)
2660 {
2661 /* If this fails, we'll still have the frag data */
2662 DCE2_AddDataToRpkt(rpkt, *rtype, seg_data, seg_len);
2663 }
2664 }
2665 else if (seg_data != NULL)
2666 {
2667 rpkt = DCE2_GetRpkt(sd->wire_pkt, *rtype, seg_data, seg_len);
2668 if (rpkt == NULL)
2669 {
2670 DCE2_Log(DCE2_LOG_TYPE__ERROR,
2671 "%s(%d) Failed to create reassembly packet.",
2672 __FILE__, __LINE__);
2673 return NULL;
2674 }
2675 }
2676
2677 return rpkt;
2678 }
2679
2680 /********************************************************************
2681 * Function: DCE2_CoSegDecode()
2682 *
2683 * Creates a reassembled packet from the segmentation buffer and
2684 * sends off to be decoded. It's also detected on since the
2685 * detection engine has yet to see this data.
2686 *
2687 * Arguments:
2688 * DCE2_SsnData *
2689 * Pointer to the session data structure.
2690 * DCE2_CoTracker *
2691 * Pointer to the relevant connection-oriented tracker.
2692 * DCE2_CoSeg *
2693 * Pointer to the client or server segmentation buffer struct.
2694 *
2695 * Returns: None
2696 *
2697 ********************************************************************/
DCE2_CoSegDecode(DCE2_SsnData * sd,DCE2_CoTracker * cot,DCE2_CoSeg * seg)2698 static void DCE2_CoSegDecode(DCE2_SsnData *sd, DCE2_CoTracker *cot, DCE2_CoSeg *seg)
2699 {
2700 const uint8_t *frag_ptr;
2701 uint16_t frag_len;
2702 SFSnortPacket *rpkt;
2703 int smb_hdr_len = DCE2_SsnFromClient(sd->wire_pkt) ? DCE2_MOCK_HDR_LEN__SMB_CLI : DCE2_MOCK_HDR_LEN__SMB_SRV;
2704 PROFILE_VARS;
2705
2706 if (DCE2_SsnFromClient(sd->wire_pkt))
2707 dce2_stats.co_cli_seg_reassembled++;
2708 else
2709 dce2_stats.co_srv_seg_reassembled++;
2710
2711 PREPROC_PROFILE_START(dce2_pstat_co_reass);
2712 rpkt = DCE2_CoGetSegRpkt(sd, DCE2_BufferData(seg->buf), DCE2_BufferLength(seg->buf));
2713 PREPROC_PROFILE_END(dce2_pstat_co_reass);
2714
2715 // FIXTHIS - don't toss data until success response to
2716 // allow for retransmission of last segment of pdu. if
2717 // we don't do it here 2 things break:
2718 // (a) we can't alert on this packet; and
2719 // (b) subsequent pdus aren't desegmented correctly.
2720 DCE2_BufferEmpty(seg->buf);
2721
2722 if (rpkt == NULL)
2723 return;
2724
2725 /* Set the start of the connection oriented pdu to where it
2726 * is in the reassembled packet */
2727 switch (sd->trans)
2728 {
2729 case DCE2_TRANS_TYPE__SMB:
2730 frag_ptr = rpkt->payload + smb_hdr_len;
2731 frag_len = rpkt->payload_size - smb_hdr_len;
2732 break;
2733
2734 case DCE2_TRANS_TYPE__TCP:
2735 case DCE2_TRANS_TYPE__HTTP_PROXY:
2736 case DCE2_TRANS_TYPE__HTTP_SERVER:
2737 frag_ptr = rpkt->payload;
2738 frag_len = rpkt->payload_size;
2739 break;
2740
2741 default:
2742 DCE2_Log(DCE2_LOG_TYPE__ERROR,
2743 "%s(%d) Invalid transport type: %d",
2744 __FILE__, __LINE__, sd->trans);
2745 return;
2746 }
2747
2748 if (DCE2_PushPkt(rpkt) != DCE2_RET__SUCCESS)
2749 {
2750 DCE2_Log(DCE2_LOG_TYPE__ERROR,
2751 "%s(%d) Failed to push packet onto packet stack.",
2752 __FILE__, __LINE__);
2753 return;
2754 }
2755
2756 /* All is good. Decode the pdu */
2757 DCE2_CoDecode(sd, cot, frag_ptr, frag_len);
2758
2759 DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Reassembled CO segmented packet\n"));
2760 DCE2_DEBUG_CODE(DCE2_DEBUG__CO, DCE2_PrintPktData(rpkt->payload, rpkt->payload_size););
2761
2762 /* Call detect since this is a reassembled packet that the
2763 * detection engine hasn't seen yet */
2764 if (!co_reassembled)
2765 DCE2_Detect(sd);
2766
2767 DCE2_PopPkt();
2768 }
2769
2770 /********************************************************************
2771 * Function: DCE2_CoSetRopts()
2772 *
2773 * Sets values necessary for the rule options.
2774 *
2775 * Arguments:
2776 * DCE2_SsnData *
2777 * Pointer to the session data structure.
2778 * DCE2_CoTracker *
2779 * Pointer to the relevant connection-oriented tracker.
2780 * DceRpcCoHdr *
2781 * Pointer to connection-oriented header in packet.
2782 *
2783 * Returns: None
2784 *
2785 ********************************************************************/
DCE2_CoSetRopts(DCE2_SsnData * sd,DCE2_CoTracker * cot,const DceRpcCoHdr * co_hdr)2786 static inline void DCE2_CoSetRopts(DCE2_SsnData *sd, DCE2_CoTracker *cot, const DceRpcCoHdr *co_hdr)
2787 {
2788 DCE2_CoFragTracker *ft = &cot->frag_tracker;
2789 int opnum = (ft->opnum != DCE2_SENTINEL) ? ft->opnum : cot->opnum;
2790 int ctx_id = (ft->ctx_id != DCE2_SENTINEL) ? ft->ctx_id : cot->ctx_id;
2791
2792 int data_byte_order =
2793 (cot->data_byte_order != DCE2_SENTINEL) ?
2794 cot->data_byte_order : (int)DceRpcCoByteOrder(co_hdr);
2795
2796 if (DCE2_CoSetIface(sd, cot, (uint16_t)ctx_id) != DCE2_RET__SUCCESS)
2797 sd->ropts.first_frag = DCE2_SENTINEL;
2798 else
2799 sd->ropts.first_frag = DceRpcCoFirstFrag(co_hdr);
2800
2801 sd->ropts.hdr_byte_order = DceRpcCoByteOrder(co_hdr);
2802 sd->ropts.data_byte_order = data_byte_order;
2803 sd->ropts.opnum = opnum;
2804 sd->ropts.stub_data = cot->stub_data;
2805 }
2806
2807 /********************************************************************
2808 * Function: DCE2_CoGetRpktType()
2809 *
2810 * Determines the type of reassembly packet we need to use
2811 * based on the transport and buffer type.
2812 *
2813 * Arguments:
2814 * DCE2_SsnData *
2815 * Pointer to the session data structure.
2816 * DCE2_BufType
2817 * The type of buffer we're using - fragmentation or
2818 * segmentation.
2819 *
2820 * Returns:
2821 * DCE2_RpktType
2822 * The type of reassembly packet type we should be using
2823 * given the transport and buffer type.
2824 *
2825 ********************************************************************/
DCE2_CoGetRpktType(DCE2_SsnData * sd,DCE2_BufType btype)2826 static inline DCE2_RpktType DCE2_CoGetRpktType(DCE2_SsnData *sd, DCE2_BufType btype)
2827 {
2828 DCE2_RpktType rtype = DCE2_RPKT_TYPE__NULL;
2829
2830 switch (sd->trans)
2831 {
2832 case DCE2_TRANS_TYPE__SMB:
2833 switch (btype)
2834 {
2835 case DCE2_BUF_TYPE__SEG:
2836 rtype = DCE2_RPKT_TYPE__SMB_CO_SEG;
2837 break;
2838
2839 case DCE2_BUF_TYPE__FRAG:
2840 rtype = DCE2_RPKT_TYPE__SMB_CO_FRAG;
2841 break;
2842
2843 default:
2844 DCE2_Log(DCE2_LOG_TYPE__ERROR,
2845 "%s(%d) Invalid buffer type: %d",
2846 __FILE__, __LINE__, btype);
2847 break;
2848 }
2849 break;
2850
2851 case DCE2_TRANS_TYPE__TCP:
2852 case DCE2_TRANS_TYPE__HTTP_PROXY:
2853 case DCE2_TRANS_TYPE__HTTP_SERVER:
2854 switch (btype)
2855 {
2856 case DCE2_BUF_TYPE__SEG:
2857 rtype = DCE2_RPKT_TYPE__TCP_CO_SEG;
2858 break;
2859
2860 case DCE2_BUF_TYPE__FRAG:
2861 rtype = DCE2_RPKT_TYPE__TCP_CO_FRAG;
2862 break;
2863
2864 default:
2865 DCE2_Log(DCE2_LOG_TYPE__ERROR,
2866 "%s(%d) Invalid buffer type: %d",
2867 __FILE__, __LINE__, btype);
2868 break;
2869 }
2870 break;
2871
2872 default:
2873 DCE2_Log(DCE2_LOG_TYPE__ERROR,
2874 "%s(%d) Invalid transport type: %d",
2875 __FILE__, __LINE__, sd->trans);
2876 break;
2877 }
2878 return rtype;
2879 }
2880
2881 /********************************************************************
2882 * Function: DCE2_CoIsSegBuf()
2883 *
2884 * Determines if the pointer passed in is within a segmentation
2885 * buffer.
2886 *
2887 * Arguments:
2888 * DCE2_SsnData *
2889 * Pointer to the session data structure.
2890 * DCE2_CoTracker *
2891 * Pointer to the relevant connection-oriented tracker.
2892 * const uint8_t *
2893 * The pointer to test.
2894 *
2895 * Returns:
2896 * int
2897 * 1 if the pointer is in a segmentation buffer.
2898 * 0 if the pointer is not within a segmentation buffer.
2899 *
2900 ********************************************************************/
DCE2_CoIsSegBuf(DCE2_SsnData * sd,DCE2_CoTracker * cot,const uint8_t * ptr)2901 static inline int DCE2_CoIsSegBuf(DCE2_SsnData *sd, DCE2_CoTracker *cot, const uint8_t *ptr)
2902 {
2903 DCE2_Buffer *seg_buf;
2904
2905 if (DCE2_SsnFromServer(sd->wire_pkt))
2906 seg_buf = cot->srv_seg.buf;
2907 else
2908 seg_buf = cot->cli_seg.buf;
2909
2910 if (DCE2_BufferIsEmpty(seg_buf))
2911 return 0;
2912
2913 /* See if we're looking at a segmentation buffer */
2914 if ((ptr < DCE2_BufferData(seg_buf)) ||
2915 (ptr > (DCE2_BufferData(seg_buf) + DCE2_BufferLength(seg_buf))))
2916 {
2917 return 0;
2918 }
2919
2920 return 1;
2921 }
2922
2923 /********************************************************************
2924 * Function: DCE2_CoSegEarlyRequest()
2925 *
2926 * Used to set rule option data if we are doing an early
2927 * reassembly on data in the segmentation buffer. If we are
2928 * taking directly from the segmentation buffer, none of the
2929 * rule option data will be set since processing doesn't get to
2930 * that point. Only do if this is a Request PDU.
2931 *
2932 * Arguments:
2933 * DCE2_CoTracker *
2934 * Pointer to the relevant connection-oriented tracker.
2935 * const uint8_t *
2936 * Pointer to the segmentation buffer data.
2937 * uint16_t
2938 * Length of the segmentation buffer data.
2939 *
2940 * Returns:
2941 * DCE2_Ret
2942 * DCE2_RET__SUCCESS if there is enough data in buffer to
2943 * set rule option data and we should continue processing.
2944 * DCE2_RET__ERROR if there is not enough data in segmentation
2945 * buffer to set rule option data and we should not
2946 * continue processing.
2947 *
2948 ********************************************************************/
DCE2_CoSegEarlyRequest(DCE2_CoTracker * cot,const uint8_t * seg_ptr,uint32_t seg_len)2949 static DCE2_Ret DCE2_CoSegEarlyRequest(DCE2_CoTracker *cot,
2950 const uint8_t *seg_ptr, uint32_t seg_len)
2951 {
2952 const DceRpcCoHdr *co_hdr;
2953 const DceRpcCoRequest *rhdr;
2954 uint16_t req_size = sizeof(DceRpcCoRequest);
2955
2956 if (seg_len < sizeof(DceRpcCoHdr))
2957 return DCE2_RET__ERROR;
2958
2959 co_hdr = (DceRpcCoHdr *)seg_ptr;
2960 DCE2_MOVE(seg_ptr, seg_len, sizeof(DceRpcCoHdr));
2961
2962 if (DceRpcCoPduType(co_hdr) != DCERPC_PDU_TYPE__REQUEST)
2963 return DCE2_RET__ERROR;
2964
2965 rhdr = (DceRpcCoRequest *)seg_ptr;
2966
2967 /* Account for possible object uuid */
2968 if (DceRpcCoObjectFlag(co_hdr))
2969 req_size += sizeof(Uuid);
2970
2971 if (seg_len < req_size)
2972 return DCE2_RET__ERROR;
2973
2974 cot->opnum = DceRpcCoOpnum(co_hdr, rhdr);
2975 cot->ctx_id = DceRpcCoCtxId(co_hdr, rhdr);
2976 cot->call_id = DceRpcCoCallId(co_hdr);
2977
2978 return DCE2_RET__SUCCESS;
2979 }
2980
2981 /********************************************************************
2982 * Function: DCE2_CoGetSegPtr()
2983 *
2984 * Returns the appropriate segmentation buffer.
2985 *
2986 * Arguments:
2987 * DCE2_SsnData *
2988 * Pointer to session data.
2989 * DCE2_CoTracker *
2990 * Pointer to connection-oriented tracker.
2991 *
2992 * Returns:
2993 * DCE2_CoSeg *
2994 * Pointer to client or server segmenation buffer.
2995 *
2996 ********************************************************************/
DCE2_CoGetSegPtr(DCE2_SsnData * sd,DCE2_CoTracker * cot)2997 static inline DCE2_CoSeg * DCE2_CoGetSegPtr(DCE2_SsnData *sd, DCE2_CoTracker *cot)
2998 {
2999 if (DCE2_SsnFromServer(sd->wire_pkt))
3000 return &cot->srv_seg;
3001
3002 return &cot->cli_seg;
3003 }
3004
3005 /********************************************************************
3006 * Function: DCE2_CoGetFragBuf()
3007 *
3008 * Returns the appropriate fragmentation buffer.
3009 *
3010 * Arguments:
3011 * DCE2_SsnData *
3012 * Pointer to session data.
3013 * DCE2_CoFragTracker *
3014 * Pointer to connection-oriented fragmentation tracker.
3015 *
3016 * Returns:
3017 * DCE2_Buffer *
3018 * Pointer to client or server fragmentation buffer.
3019 *
3020 ********************************************************************/
DCE2_CoGetFragBuf(DCE2_SsnData * sd,DCE2_CoFragTracker * ft)3021 static inline DCE2_Buffer * DCE2_CoGetFragBuf(DCE2_SsnData *sd, DCE2_CoFragTracker *ft)
3022 {
3023 if (DCE2_SsnFromServer(sd->wire_pkt))
3024 return ft->srv_stub_buf;
3025
3026 return ft->cli_stub_buf;
3027 }
3028
DCE2_CoGetAuthLen(DCE2_SsnData * sd,const DceRpcCoHdr * co_hdr,const uint8_t * frag_ptr,uint16_t frag_len)3029 static int DCE2_CoGetAuthLen(DCE2_SsnData *sd, const DceRpcCoHdr *co_hdr,
3030 const uint8_t *frag_ptr, uint16_t frag_len)
3031 {
3032 DceRpcCoAuthVerifier *auth_hdr;
3033 uint16_t auth_len = DceRpcCoAuthLen(co_hdr);
3034
3035 if (auth_len == 0)
3036 return 0;
3037
3038 auth_len += sizeof(DceRpcCoAuthVerifier);
3039
3040 /* This means the auth len was bogus */
3041 if (auth_len > frag_len)
3042 {
3043 DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_SIZE, dce2_pdu_types[DceRpcCoPduType(co_hdr)],
3044 frag_len, auth_len);
3045 return -1;
3046 }
3047
3048 auth_hdr = (DceRpcCoAuthVerifier *)(frag_ptr + (frag_len - auth_len));
3049 if (DceRpcCoAuthLevel(auth_hdr) == DCERPC_CO_AUTH_LEVEL__PKT_PRIVACY)
3050 {
3051 /* Data is encrypted - don't inspect */
3052 return -1;
3053 }
3054
3055 auth_len += DceRpcCoAuthPad(auth_hdr);
3056
3057 /* This means the auth pad len was bogus */
3058 if (auth_len > frag_len)
3059 {
3060 DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_SIZE, dce2_pdu_types[DceRpcCoPduType(co_hdr)],
3061 frag_len, auth_len);
3062 return -1;
3063 }
3064
3065 return (int)auth_len;
3066 }
3067