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