2 * Routines for DCERPC over SMB packet disassembly
3 * Copyright 2001-2003, Tim Potter <tpot@samba.org>
5 * $Id: packet-dcerpc-nt.c,v 1.68 2003/02/24 01:22:14 guy Exp $
7 * Ethereal - Network traffic analyzer
8 * By Gerald Combs <gerald@ethereal.com>
9 * Copyright 1998 Gerald Combs
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
31 #include <epan/packet.h>
32 #include "packet-dcerpc.h"
33 #include "packet-dcerpc-nt.h"
35 #include "packet-smb-common.h" /* for dissect_smb_64bit_time() */
38 * This file contains helper routines that are used by the DCERPC over SMB
39 * dissectors for ethereal.
42 /* Parse some common RPC structures */
44 gint ett_nt_unicode_string = -1; /* FIXME: make static */
46 /* Dissect a counted string as a callback to dissect_ndr_pointer_cb() */
48 static int hf_nt_cs_len = -1;
49 static int hf_nt_cs_size = -1;
52 dissect_ndr_counted_string_cb(tvbuff_t *tvb, int offset,
53 packet_info *pinfo, proto_tree *tree,
54 char *drep, int hf_index,
55 dcerpc_callback_fnct_t *callback,
58 dcerpc_info *di = pinfo->private_data;
61 /* Structure starts with short, but is aligned for longs */
65 if (di->conformant_run)
72 [size_is(size/2), length_is(len/2), ptr] unsigned short *string;
77 offset = dissect_ndr_uint16(tvb, offset, pinfo, tree, drep,
80 offset = dissect_ndr_uint16(tvb, offset, pinfo, tree, drep,
81 hf_nt_cs_size, &size);
83 offset = dissect_ndr_pointer_cb(tvb, offset, pinfo, tree, drep,
84 dissect_ndr_wchar_cvstring, NDR_POINTER_UNIQUE,
85 "Character Array", hf_index, callback, callback_args);
90 static gint ett_nt_counted_string = -1;
93 dissect_ndr_counted_string_helper(tvbuff_t *tvb, int offset,
94 packet_info *pinfo, proto_tree *tree,
95 char *drep, int hf_index, int levels,
99 proto_tree *subtree = tree;
103 item = proto_tree_add_text(
104 tree, tvb, offset, 0,
105 proto_registrar_get_name(hf_index));
107 subtree = proto_item_add_subtree(item, ett_nt_counted_string);
111 * Add 2 levels, so that the string gets attached to the
112 * "Character Array" top-level item and to the top-level item
115 return dissect_ndr_counted_string_cb(
116 tvb, offset, pinfo, subtree, drep, hf_index,
117 cb_str_postprocess, GINT_TO_POINTER(2 + levels));
120 /* Dissect a counted string in-line. */
123 dissect_ndr_counted_string(tvbuff_t *tvb, int offset,
124 packet_info *pinfo, proto_tree *tree,
125 char *drep, int hf_index, int levels)
127 return dissect_ndr_counted_string_helper(
128 tvb, offset, pinfo, tree, drep, hf_index, levels, TRUE);
131 /* Dissect a counted string as a callback to dissect_ndr_pointer().
132 This doesn't add a adds a proto item and subtreee for the string as
133 the pointer dissection already creates one. */
136 dissect_ndr_counted_string_ptr(tvbuff_t *tvb, int offset,
137 packet_info *pinfo, proto_tree *tree,
140 dcerpc_info *di = pinfo->private_data;
142 return dissect_ndr_counted_string_helper(
143 tvb, offset, pinfo, tree, drep, di->hf_index, 0, FALSE);
146 /* Dissect a counted byte_array as a callback to dissect_ndr_pointer_cb() */
148 static gint ett_nt_counted_byte_array = -1;
150 /* Dissect a counted byte array in-line. */
153 dissect_ndr_counted_byte_array(tvbuff_t *tvb, int offset,
154 packet_info *pinfo, proto_tree *tree,
155 char *drep, int hf_index)
157 dcerpc_info *di = pinfo->private_data;
162 /* Structure starts with short, but is aligned for longs */
166 if (di->conformant_run)
169 item = proto_tree_add_text(tree, tvb, offset, 0,
170 proto_registrar_get_name(hf_index));
172 subtree = proto_item_add_subtree(item, ett_nt_counted_byte_array);
178 [size_is(size), length_is(len), ptr] unsigned char *string;
179 } WHATEVER_THIS_IS_CALLED;
183 offset = dissect_ndr_uint16(tvb, offset, pinfo, subtree, drep,
186 offset = dissect_ndr_uint16(tvb, offset, pinfo, subtree, drep,
187 hf_nt_cs_size, &size);
189 offset = dissect_ndr_pointer(tvb, offset, pinfo, subtree, drep,
190 dissect_ndr_char_cvstring, NDR_POINTER_UNIQUE,
191 "Byte Array", hf_index);
196 /* This function is used to dissect a DCERPC encoded 64 bit time value.
197 XXX it should be fixed both here and in dissect_smb_64bit_time so
198 it can handle both BIG and LITTLE endian encodings
201 dissect_ndr_nt_NTTIME (tvbuff_t *tvb, int offset,
202 packet_info *pinfo, proto_tree *tree,
203 char *drep _U_, int hf_index)
207 di=pinfo->private_data;
208 if(di->conformant_run){
209 /*just a run to handle conformant arrays, nothing to dissect */
215 offset = dissect_smb_64bit_time(tvb, tree, offset, hf_index);
219 /* Define this symbol to display warnings about request/response and
220 policy handle hash table collisions. This happens when a packet with
221 the same conversation, smb fid and dcerpc call id occurs. I think this
222 is due to a bug in the dcerpc/smb fragment reassembly code. */
224 #undef DEBUG_HASH_COLL
227 * Policy handle hashing
231 guint8 policy_hnd[20];
235 guint32 open_frame, close_frame; /* Frame numbers for open/close */
236 char *name; /* Name of policy handle */
239 #define POL_HASH_INIT_COUNT 100
241 static GHashTable *pol_hash;
242 static GMemChunk *pol_hash_key_chunk;
243 static GMemChunk *pol_hash_value_chunk;
247 static guint pol_hash_fn(gconstpointer k)
249 const pol_hash_key *key = (const pol_hash_key *)k;
251 /* Bytes 4-7 of the policy handle are a timestamp so should make a
252 reasonable hash value */
254 return key->policy_hnd[4] + (key->policy_hnd[5] << 8) +
255 (key->policy_hnd[6] << 16) + (key->policy_hnd[7] << 24);
258 /* Return true if a policy handle is all zeros */
260 static gboolean is_null_pol(e_ctx_hnd *policy_hnd)
262 static guint8 null_policy_hnd[20];
264 return memcmp(policy_hnd, null_policy_hnd, 20) == 0;
267 /* Hash compare function */
269 static gint pol_hash_compare(gconstpointer k1, gconstpointer k2)
271 const pol_hash_key *key1 = (const pol_hash_key *)k1;
272 const pol_hash_key *key2 = (const pol_hash_key *)k2;
274 return memcmp(key1->policy_hnd, key2->policy_hnd,
275 sizeof(key1->policy_hnd)) == 0;
278 /* Store the open and close frame numbers of a policy handle */
280 void dcerpc_smb_store_pol_pkts(e_ctx_hnd *policy_hnd, guint32 open_frame,
284 pol_hash_value *value;
286 if (is_null_pol(policy_hnd) || (open_frame == 0 && close_frame == 0))
289 /* Look up existing value */
291 key = g_mem_chunk_alloc(pol_hash_key_chunk);
293 memcpy(&key->policy_hnd, policy_hnd, sizeof(key->policy_hnd));
295 if ((value = g_hash_table_lookup(pol_hash, key))) {
297 /* Update existing value */
300 #ifdef DEBUG_HASH_COLL
301 if (value->open_frame != open_frame)
302 g_warning("dcerpc_smb: pol_hash open frame collision %d/%d\n", value->open_frame, open_frame);
304 value->open_frame = open_frame;
308 #ifdef DEBUG_HASH_COLL
309 if (value->close_frame != close_frame)
310 g_warning("dcerpc_smb: pol_hash close frame collision %d/%d\n", value->close_frame, close_frame);
312 value->close_frame = close_frame;
318 /* Create a new value */
320 value = g_mem_chunk_alloc(pol_hash_value_chunk);
322 value->open_frame = open_frame;
323 value->close_frame = close_frame;
327 g_hash_table_insert(pol_hash, key, value);
330 /* Store a text string with a policy handle */
332 void dcerpc_smb_store_pol_name(e_ctx_hnd *policy_hnd, char *name)
335 pol_hash_value *value;
337 if (is_null_pol(policy_hnd))
340 /* Look up existing value */
342 key = g_mem_chunk_alloc(pol_hash_key_chunk);
344 memcpy(&key->policy_hnd, policy_hnd, sizeof(key->policy_hnd));
346 if ((value = g_hash_table_lookup(pol_hash, key))) {
348 /* Update existing value */
350 if (value->name && name) {
351 #ifdef DEBUG_HASH_COLL
352 if (strcmp(value->name, name) != 0)
353 g_warning("dcerpc_smb: pol_hash name collision %s/%s\n", value->name, name);
358 value->name = strdup(name);
363 /* Create a new value */
365 value = g_mem_chunk_alloc(pol_hash_value_chunk);
367 value->open_frame = 0;
368 value->close_frame = 0;
371 value->name = strdup(name);
373 value->name = strdup("<UNKNOWN>");
375 g_hash_table_insert(pol_hash, key, value);
378 /* Retrieve a policy handle */
380 gboolean dcerpc_smb_fetch_pol(e_ctx_hnd *policy_hnd, char **name,
381 guint32 *open_frame, guint32 *close_frame)
384 pol_hash_value *value;
386 /* Prevent uninitialised return vars */
397 /* Look up existing value */
399 memcpy(&key.policy_hnd, policy_hnd, sizeof(key.policy_hnd));
401 value = g_hash_table_lookup(pol_hash, &key);
403 /* Return name and frame numbers */
410 *open_frame = value->open_frame;
413 *close_frame = value->close_frame;
416 return value != NULL;
419 /* Iterator to free a policy handle key/value pair */
421 static void free_pol_keyvalue(gpointer key _U_, gpointer value,
422 gpointer user_data _U_)
424 pol_hash_value *pol_value = (pol_hash_value *)value;
428 if (pol_value->name) {
429 free(pol_value->name);
430 pol_value->name = NULL;
434 /* Initialise policy handle hash */
436 static void init_pol_hash(void)
438 /* Initialise memory chunks */
440 if (pol_hash_key_chunk)
441 g_mem_chunk_destroy(pol_hash_key_chunk);
443 pol_hash_key_chunk = g_mem_chunk_new(
444 "Policy handle hash keys", sizeof(pol_hash_key),
445 POL_HASH_INIT_COUNT * sizeof(pol_hash_key), G_ALLOC_ONLY);
447 if (pol_hash_value_chunk)
448 g_mem_chunk_destroy(pol_hash_value_chunk);
450 pol_hash_value_chunk = g_mem_chunk_new(
451 "Policy handle hash values", sizeof(pol_hash_value),
452 POL_HASH_INIT_COUNT * sizeof(pol_hash_value), G_ALLOC_ONLY);
454 /* Initialise hash table */
457 g_hash_table_foreach(pol_hash, free_pol_keyvalue, NULL);
458 g_hash_table_destroy(pol_hash);
461 pol_hash = g_hash_table_new(pol_hash_fn, pol_hash_compare);
464 /* Check if there is unparsed data remaining in a frame and display an
465 error. I guess this could be made into an exception like the malformed
466 frame exception. For the DCERPC over SMB dissectors a long frame
467 indicates a bug in a dissector. */
469 void dcerpc_smb_check_long_frame(tvbuff_t *tvb, int offset,
470 packet_info *pinfo, proto_tree *tree)
472 if (tvb_length_remaining(tvb, offset) != 0) {
475 tree, tvb, offset, tvb_length_remaining(tvb, offset),
476 "[Long frame (%d bytes): SPOOLSS]",
477 tvb_length_remaining(tvb, offset));
479 if (check_col(pinfo->cinfo, COL_INFO))
480 col_append_fstr(pinfo->cinfo, COL_INFO,
481 "[Long frame (%d bytes): SPOOLSS]",
482 tvb_length_remaining(tvb, offset));
486 /* Dissect a NT status code */
489 dissect_ntstatus(tvbuff_t *tvb, gint offset, packet_info *pinfo,
490 proto_tree *tree, char *drep,
491 int hfindex, guint32 *pdata)
495 offset = dissect_ndr_uint32(tvb, offset, pinfo, tree, drep,
498 if (status != 0 && check_col(pinfo->cinfo, COL_INFO))
499 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
500 val_to_str(status, NT_errors,
501 "Unknown error 0x%08x"));
508 /* Dissect a DOS status code */
511 dissect_doserror(tvbuff_t *tvb, gint offset, packet_info *pinfo,
512 proto_tree *tree, char *drep,
513 int hfindex, guint32 *pdata)
517 offset = dissect_ndr_uint32(tvb, offset, pinfo, tree, drep,
520 if (status != 0 && check_col(pinfo->cinfo, COL_INFO))
521 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
522 val_to_str(status, DOS_errors,
523 "Unknown error 0x%08x"));
530 /* Dissect a NT policy handle */
532 static int hf_nt_policy_open_frame = -1;
533 static int hf_nt_policy_close_frame = -1;
535 static gint ett_nt_policy_hnd = -1;
538 dissect_nt_policy_hnd(tvbuff_t *tvb, gint offset, packet_info *pinfo,
539 proto_tree *tree, char *drep, int hfindex,
540 e_ctx_hnd *pdata, gboolean is_open, gboolean is_close)
545 guint32 open_frame = 0, close_frame = 0;
547 int old_offset = offset;
549 /* Add to proto tree */
551 item = proto_tree_add_text(tree, tvb, offset, sizeof(e_ctx_hnd),
554 subtree = proto_item_add_subtree(item, ett_nt_policy_hnd);
556 offset = dissect_ndr_ctx_hnd(tvb, offset, pinfo, subtree, drep,
559 /* Store request/reply information */
561 dcerpc_smb_store_pol_pkts(&hnd, 0, is_close ? pinfo->fd->num : 0);
562 dcerpc_smb_store_pol_pkts(&hnd, is_open ? pinfo->fd->num: 0, 0);
564 /* Insert request/reply information if known */
566 if (dcerpc_smb_fetch_pol(&hnd, &name, &open_frame, &close_frame)) {
570 subtree, hf_nt_policy_open_frame, tvb,
571 old_offset, sizeof(e_ctx_hnd), open_frame);
575 subtree, hf_nt_policy_close_frame, tvb,
576 old_offset, sizeof(e_ctx_hnd), close_frame);
579 proto_item_append_text(item, ": %s", name);
588 /* Some helper routines to dissect a range of uint8 characters. I don't
589 think these are "official" NDR representations and are probably specific
590 to NT so for the moment they're put here instead of in packet-dcerpc.c
591 and packet-dcerpc-ndr.c. */
594 dissect_dcerpc_uint8s(tvbuff_t *tvb, gint offset, packet_info *pinfo _U_,
595 proto_tree *tree, char *drep, int hfindex,
596 int length, const guint8 **pdata)
600 data = (const guint8 *)tvb_get_ptr(tvb, offset, length);
603 proto_tree_add_item (tree, hfindex, tvb, offset, length, (drep[0] & 0x10));
609 return offset + length;
613 dissect_ndr_uint8s(tvbuff_t *tvb, gint offset, packet_info *pinfo,
614 proto_tree *tree, char *drep,
615 int hfindex, int length, const guint8 **pdata)
619 di=pinfo->private_data;
620 if(di->conformant_run){
621 /* just a run to handle conformant arrays, no scalars to dissect */
625 /* no alignment needed */
626 return dissect_dcerpc_uint8s(tvb, offset, pinfo,
627 tree, drep, hfindex, length, pdata);
631 dissect_dcerpc_uint16s(tvbuff_t *tvb, gint offset, packet_info *pinfo _U_,
632 proto_tree *tree, char *drep, int hfindex,
636 proto_tree_add_item (tree, hfindex, tvb, offset, length * 2, (drep[0] & 0x10));
639 return offset + length * 2;
643 dissect_ndr_uint16s(tvbuff_t *tvb, gint offset, packet_info *pinfo,
644 proto_tree *tree, char *drep,
645 int hfindex, int length)
649 di=pinfo->private_data;
650 if(di->conformant_run){
651 /* just a run to handle conformant arrays, no scalars to dissect */
658 return dissect_dcerpc_uint16s(tvb, offset, pinfo,
659 tree, drep, hfindex, length);
663 * Helper routines for dissecting NDR strings
666 void cb_str_postprocess(packet_info *pinfo, proto_tree *tree _U_,
667 proto_item *item, tvbuff_t *tvb,
668 int start_offset, int end_offset,
671 gint options = GPOINTER_TO_INT(callback_args);
672 gint levels = CB_STR_ITEM_LEVELS(options);
675 /* Align start_offset on 4-byte boundary. */
677 if (start_offset % 4)
678 start_offset += 4 - (start_offset % 4);
680 /* Get string value */
682 if ((end_offset - start_offset) <= 12)
683 return; /* XXX: Use unistr2 dissector instead? */
685 s = tvb_fake_unicode(
686 tvb, start_offset + 12, (end_offset - start_offset - 12) / 2,
689 /* Append string to COL_INFO */
691 if (options & CB_STR_COL_INFO) {
692 if (check_col(pinfo->cinfo, COL_INFO))
693 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", s);
696 /* Append string to upper-level proto_items */
697 if (levels > 0 && item) {
698 proto_item_append_text(item, ": %s", s);
702 proto_item_append_text(item, ": %s", s);
706 proto_item_append_text(item, " %s", s);
713 /* Save string to dcv->private_data */
715 if (options & CB_STR_SAVE) {
716 dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
717 dcerpc_call_value *dcv = (dcerpc_call_value *)di->call_data;
719 dcv->private_data = g_strdup(s);
725 /* Dissect a pointer to a NDR string and append the string value to the
728 int dissect_ndr_str_pointer_item(tvbuff_t *tvb, gint offset,
729 packet_info *pinfo, proto_tree *tree,
730 char *drep, int type, char *text,
731 int hf_index, int levels)
733 return dissect_ndr_pointer_cb(
734 tvb, offset, pinfo, tree, drep,
735 dissect_ndr_wchar_cvstring, type, text, hf_index,
736 cb_str_postprocess, GINT_TO_POINTER(levels + 1));
740 * Register ett/hf values and perform DCERPC over SMB specific
743 void dcerpc_smb_init(int proto_dcerpc)
745 static hf_register_info hf[] = {
747 /* String handling */
750 { "Size", "nt.str.size", FT_UINT16, BASE_DEC,
751 NULL, 0x0, "Size of string in short integers",
755 { "Length", "nt.str.len", FT_UINT16, BASE_DEC,
756 NULL, 0x0, "Length of string in short integers",
761 { &hf_nt_policy_open_frame,
762 { "Frame handle opened", "dcerpc.nt.open_frame",
763 FT_FRAMENUM, BASE_NONE, NULL, 0x0,
764 "Frame handle opened", HFILL }},
766 { &hf_nt_policy_close_frame,
767 { "Frame handle close", "dcerpc.nt.close_frame",
768 FT_FRAMENUM, BASE_NONE, NULL, 0x0,
769 "Frame handle closed", HFILL }},
772 static gint *ett[] = {
773 &ett_nt_unicode_string,
774 &ett_nt_counted_string,
775 &ett_nt_counted_byte_array,
779 /* Register ett's and hf's */
781 proto_register_subtree_array(ett, array_length(ett));
782 proto_register_field_array(proto_dcerpc, hf, array_length(hf));
784 /* Initialise policy handle hash */