2 dissect_ndr_nt_SID_with_options see comment.
5 * Routines for DCERPC over SMB packet disassembly
6 * Copyright 2001-2003, Tim Potter <tpot@samba.org>
10 * Ethereal - Network traffic analyzer
11 * By Gerald Combs <gerald@ethereal.com>
12 * Copyright 1998 Gerald Combs
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
36 #include <epan/packet.h>
37 #include "packet-dcerpc.h"
38 #include "packet-dcerpc-nt.h"
39 #include "packet-windows-common.h"
42 * This file contains helper routines that are used by the DCERPC over SMB
43 * dissectors for ethereal.
47 * Used by several dissectors.
49 const value_string platform_id_vals[] = {
52 { 500, "Windows NT" },
58 /* Parse some common RPC structures */
60 gint ett_nt_unicode_string = -1; /* FIXME: make static */
62 /* Dissect a counted string as a callback to dissect_ndr_pointer_cb() */
64 static int hf_nt_cs_len = -1;
65 static int hf_nt_cs_size = -1;
68 dissect_ndr_counted_string_cb(tvbuff_t *tvb, int offset,
69 packet_info *pinfo, proto_tree *tree,
70 guint8 *drep, int hf_index,
71 dcerpc_callback_fnct_t *callback,
74 dcerpc_info *di = pinfo->private_data;
77 /* Structure starts with short, but is aligned for longs */
81 if (di->conformant_run)
88 [size_is(size/2), length_is(len/2), ptr] unsigned short *string;
93 offset = dissect_ndr_uint16(tvb, offset, pinfo, tree, drep,
96 offset = dissect_ndr_uint16(tvb, offset, pinfo, tree, drep,
97 hf_nt_cs_size, &size);
99 offset = dissect_ndr_pointer_cb(tvb, offset, pinfo, tree, drep,
100 dissect_ndr_wchar_cvstring, NDR_POINTER_UNIQUE,
101 "Character Array", hf_index, callback, callback_args);
106 static gint ett_nt_counted_string = -1;
109 dissect_ndr_counted_string_helper(tvbuff_t *tvb, int offset,
110 packet_info *pinfo, proto_tree *tree,
111 guint8 *drep, int hf_index, int levels,
112 gboolean add_subtree)
115 proto_tree *subtree = tree;
119 item = proto_tree_add_text(
120 tree, tvb, offset, 0,
121 proto_registrar_get_name(hf_index));
123 subtree = proto_item_add_subtree(item, ett_nt_counted_string);
127 * Add 2 levels, so that the string gets attached to the
128 * "Character Array" top-level item and to the top-level item
131 return dissect_ndr_counted_string_cb(
132 tvb, offset, pinfo, subtree, drep, hf_index,
133 cb_wstr_postprocess, GINT_TO_POINTER(2 + levels));
136 /* Dissect a counted string in-line. */
139 dissect_ndr_counted_string(tvbuff_t *tvb, int offset,
140 packet_info *pinfo, proto_tree *tree,
141 guint8 *drep, int hf_index, int levels)
143 return dissect_ndr_counted_string_helper(
144 tvb, offset, pinfo, tree, drep, hf_index, levels, TRUE);
147 /* Dissect a counted string as a callback to dissect_ndr_pointer().
148 This doesn't add a adds a proto item and subtreee for the string as
149 the pointer dissection already creates one. */
152 dissect_ndr_counted_string_ptr(tvbuff_t *tvb, int offset,
153 packet_info *pinfo, proto_tree *tree,
156 dcerpc_info *di = pinfo->private_data;
158 return dissect_ndr_counted_string_helper(
159 tvb, offset, pinfo, tree, drep, di->hf_index, 0, FALSE);
162 /* Dissect a counted byte_array as a callback to dissect_ndr_pointer_cb() */
164 static gint ett_nt_counted_byte_array = -1;
166 /* Dissect a counted byte array in-line. */
169 dissect_ndr_counted_byte_array_cb(tvbuff_t *tvb, int offset,
170 packet_info *pinfo, proto_tree *tree,
171 guint8 *drep, int hf_index,
172 dcerpc_callback_fnct_t *callback,
175 dcerpc_info *di = pinfo->private_data;
180 /* Structure starts with short, but is aligned for longs */
184 if (di->conformant_run)
187 item = proto_tree_add_text(tree, tvb, offset, 0,
188 proto_registrar_get_name(hf_index));
190 subtree = proto_item_add_subtree(item, ett_nt_counted_byte_array);
196 [size_is(size), length_is(len), ptr] unsigned char *string;
197 } WHATEVER_THIS_IS_CALLED;
201 offset = dissect_ndr_uint16(tvb, offset, pinfo, subtree, drep,
204 offset = dissect_ndr_uint16(tvb, offset, pinfo, subtree, drep,
205 hf_nt_cs_size, &size);
207 offset = dissect_ndr_pointer_cb(tvb, offset, pinfo, subtree, drep,
208 dissect_ndr_char_cvstring, NDR_POINTER_UNIQUE,
209 "Byte Array", hf_index, callback, callback_args);
214 static void cb_byte_array_postprocess(packet_info *pinfo, proto_tree *tree _U_,
215 proto_item *item, tvbuff_t *tvb,
216 int start_offset, int end_offset,
219 gint options = GPOINTER_TO_INT(callback_args);
220 gint levels = CB_STR_ITEM_LEVELS(options);
223 /* Align start_offset on 4-byte boundary. */
225 if (start_offset % 4)
226 start_offset += 4 - (start_offset % 4);
228 /* Get byte array value */
230 if ((end_offset - start_offset) <= 12)
233 s = tvb_bytes_to_str(
234 tvb, start_offset + 12, (end_offset - start_offset - 12) );
236 /* Append string to COL_INFO */
238 if (options & CB_STR_COL_INFO) {
239 if (check_col(pinfo->cinfo, COL_INFO))
240 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", s);
243 /* Append string to upper-level proto_items */
245 if (levels > 0 && item && s && s[0]) {
246 proto_item_append_text(item, ": %s", s);
250 proto_item_append_text(item, ": %s", s);
254 proto_item_append_text(item, " %s", s);
263 dissect_ndr_counted_byte_array(tvbuff_t *tvb, int offset,
264 packet_info *pinfo, proto_tree *tree,
265 guint8 *drep, int hf_index, int levels)
267 return dissect_ndr_counted_byte_array_cb(
268 tvb, offset, pinfo, tree, drep, hf_index, cb_byte_array_postprocess, GINT_TO_POINTER(2 + levels));
271 /* Dissect a counted ascii string in-line. */
272 static gint ett_nt_counted_ascii_string = -1;
275 dissect_ndr_counted_ascii_string_cb(tvbuff_t *tvb, int offset,
276 packet_info *pinfo, proto_tree *tree,
277 guint8 *drep, int hf_index,
278 dcerpc_callback_fnct_t *callback,
281 dcerpc_info *di = pinfo->private_data;
286 /* Structure starts with short, but is aligned for longs */
290 if (di->conformant_run)
293 item = proto_tree_add_text(tree, tvb, offset, 0,
294 proto_registrar_get_name(hf_index));
296 subtree = proto_item_add_subtree(item, ett_nt_counted_ascii_string);
302 [size_is(size), length_is(len), ptr] unsigned char *string;
303 } WHATEVER_THIS_IS_CALLED;
307 offset = dissect_ndr_uint16(tvb, offset, pinfo, subtree, drep,
310 offset = dissect_ndr_uint16(tvb, offset, pinfo, subtree, drep,
311 hf_nt_cs_size, &size);
313 offset = dissect_ndr_pointer_cb(tvb, offset, pinfo, subtree, drep,
314 dissect_ndr_char_cvstring, NDR_POINTER_UNIQUE,
315 "Ascii String", hf_index, callback, callback_args);
321 dissect_ndr_counted_ascii_string(tvbuff_t *tvb, int offset,
322 packet_info *pinfo, proto_tree *tree,
323 guint8 *drep, int hf_index, int levels)
325 return dissect_ndr_counted_ascii_string_cb(
326 tvb, offset, pinfo, tree, drep, hf_index, cb_str_postprocess, GINT_TO_POINTER(2 + levels));
329 static int hf_nt_guid = -1;
332 dissect_nt_GUID(tvbuff_t *tvb, int offset,
333 packet_info *pinfo, proto_tree *tree,
336 offset=dissect_ndr_uuid_t(tvb, offset, pinfo, tree, drep, hf_nt_guid, NULL);
341 /* This function is used to dissect a DCERPC encoded 64 bit time value.
342 XXX it should be fixed both here and in dissect_nt_64bit_time so
343 it can handle both BIG and LITTLE endian encodings
346 dissect_ndr_nt_NTTIME (tvbuff_t *tvb, int offset,
347 packet_info *pinfo, proto_tree *tree,
348 guint8 *drep _U_, int hf_index)
352 di=pinfo->private_data;
353 if(di->conformant_run){
354 /*just a run to handle conformant arrays, nothing to dissect */
360 offset = dissect_nt_64bit_time(tvb, tree, offset, hf_index);
364 /* Define this symbol to display warnings about request/response and
365 policy handle hash table collisions. This happens when a packet with
366 the same conversation, smb fid and dcerpc call id occurs. I think this
367 is due to a bug in the dcerpc/smb fragment reassembly code. */
369 #undef DEBUG_HASH_COLL
372 * Policy handle hashing.
374 * We hash based on the policy handle value; the items in the hash table
375 * are lists of policy handle information about one or more policy
376 * handles with that value. We have multiple values in case a given
377 * policy handle is opened in frame N, closed in frame M, and re-opened
378 * in frame O, where N < M < O.
380 * XXX - we really should also use a DCE RPC conversation/session handle
381 * of some sort, in case two separate sessions have the same handle
382 * value. A transport-layer conversation might not be sufficient, as you
383 * might, for example, have multiple pipes in a single SMB connection,
384 * and you might have the same handle opened and closed separately on
387 * The policy handle information has "first frame" and "last frame"
388 * information; the entry should be used when dissecting a given frame
389 * only if that frame is within the interval [first frame,last frame].
390 * The list is sorted by "first frame".
392 * This doesn't handle the case of a handle being opened in frame N and
393 * re-opened in frame M, where N < M, with no intervening close, but I'm
394 * not sure anything can handle that if it's within the same DCE RPC
395 * session (if it's not, the conversation/session handle would fix that).
399 guint8 policy_hnd[20];
402 typedef struct pol_value {
403 struct pol_value *next; /* Next entry in hash bucket */
404 guint32 open_frame, close_frame; /* Frame numbers for open/close */
405 guint32 first_frame; /* First frame in which this instance was seen */
406 guint32 last_frame; /* Last frame in which this instance was seen */
407 char *name; /* Name of policy handle */
411 pol_value *list; /* List of policy handle entries */
414 #define POL_HASH_INIT_COUNT 100
416 static GHashTable *pol_hash;
417 static GMemChunk *pol_hash_key_chunk;
418 static GMemChunk *pol_value_chunk;
419 static GMemChunk *pol_hash_value_chunk;
423 static guint pol_hash_fn(gconstpointer k)
425 const pol_hash_key *key = (const pol_hash_key *)k;
427 /* Bytes 4-7 of the policy handle are a timestamp so should make a
428 reasonable hash value */
430 return key->policy_hnd[4] + (key->policy_hnd[5] << 8) +
431 (key->policy_hnd[6] << 16) + (key->policy_hnd[7] << 24);
434 /* Return true if a policy handle is all zeros */
436 static gboolean is_null_pol(e_ctx_hnd *policy_hnd)
438 static guint8 null_policy_hnd[20];
440 return memcmp(policy_hnd, null_policy_hnd, 20) == 0;
443 /* Hash compare function */
445 static gint pol_hash_compare(gconstpointer k1, gconstpointer k2)
447 const pol_hash_key *key1 = (const pol_hash_key *)k1;
448 const pol_hash_key *key2 = (const pol_hash_key *)k2;
450 return memcmp(key1->policy_hnd, key2->policy_hnd,
451 sizeof(key1->policy_hnd)) == 0;
455 * Look up the instance of a policy handle value in whose range of frames
456 * the specified frame falls.
458 static pol_value *find_pol_handle(e_ctx_hnd *policy_hnd, guint32 frame,
459 pol_hash_value **valuep)
464 memcpy(&key.policy_hnd, policy_hnd, sizeof(key.policy_hnd));
465 if ((*valuep = g_hash_table_lookup(pol_hash, &key))) {
467 * Look for the first value such that both:
469 * 1) the first frame in which it was seen is
470 * <= the specified frame;
472 * 2) the last frame in which it was seen is
473 * either unknown (meaning we haven't yet
474 * seen a close or another open of the
475 * same handle, which is assumed to imply
476 * an intervening close that wasn't captured)
477 * or is >= the specified frame.
479 * If there's more than one such frame, that's the
480 * case where a handle is opened in frame N and
481 * reopened in frame M, with no intervening close;
482 * there is no right answer for that, so the instance
483 * opened in frame N is as right as anything else.
485 for (pol = (*valuep)->list; pol != NULL; pol = pol->next) {
486 if (pol->first_frame <= frame &&
487 (pol->last_frame == 0 ||
488 pol->last_frame >= frame))
489 break; /* found one */
494 * The handle isn't in the hash table.
500 static void add_pol_handle(e_ctx_hnd *policy_hnd, guint32 frame,
501 pol_value *pol, pol_hash_value *value)
504 pol_value *polprev, *polnext;
508 * There's no hash value; create one, put the new
509 * value at the beginning of its policy handle list,
510 * and put the hash value in the policy handle hash
513 value = g_mem_chunk_alloc(pol_hash_value_chunk);
516 key = g_mem_chunk_alloc(pol_hash_key_chunk);
517 memcpy(&key->policy_hnd, policy_hnd, sizeof(key->policy_hnd));
518 g_hash_table_insert(pol_hash, key, value);
521 * Put the new value in the hash value's policy handle
522 * list so that it's sorted by the first frame in
525 * Search for the first entry whose first frame number
526 * is greater than the current frame number, if any.
528 for (polnext = value->list, polprev = NULL;
529 polnext != NULL && polnext->first_frame <= frame;
530 polprev = polnext, polnext = polnext->next)
534 * "polprev" points to the entry in the list after
535 * which we should put the new entry; if it's null,
536 * that means we should put it at the beginning of
545 * "polnext" points to the entry in the list before
546 * which we should put the new entry; if it's null,
547 * that means we should put it at the end of the list.
553 /* Store the open and close frame numbers of a policy handle */
555 void dcerpc_smb_store_pol_pkts(e_ctx_hnd *policy_hnd, packet_info *pinfo,
556 gboolean is_open, gboolean is_close)
558 pol_hash_value *value;
562 * By the time the first pass is done, the policy handle database
563 * has been completely constructed. If we've already seen this
564 * frame, there's nothing to do.
566 if (pinfo->fd->flags.visited)
569 if (is_null_pol(policy_hnd))
572 /* Look up existing value */
573 pol = find_pol_handle(policy_hnd, pinfo->fd->num, &value);
577 * Update the existing value as appropriate.
581 * This is an open; we assume that we missed
582 * a close of this handle, so we set its
583 * "last frame" value and act as if we didn't
586 * XXX - note that we might be called twice for
587 * the same operation (see "dissect_pipe_dcerpc()",
588 * which calls the DCE RPC dissector twice), so we
589 * must first check to see if this is a handle we
592 * We check whether this handle's "first frame"
593 * frame number is this frame and its "last frame
594 * is 0; if so, this is presumably a duplicate call,
595 * and we don't do an implicit close.
597 if (pol->first_frame == pinfo->fd->num &&
598 pol->last_frame == 0)
600 pol->last_frame = pinfo->fd->num;
604 pol->close_frame = pinfo->fd->num;
605 pol->last_frame = pinfo->fd->num;
611 /* Create a new value */
613 pol = g_mem_chunk_alloc(pol_value_chunk);
615 pol->open_frame = is_open ? pinfo->fd->num : 0;
616 pol->close_frame = is_close ? pinfo->fd->num : 0;
617 pol->first_frame = pinfo->fd->num;
618 pol->last_frame = pol->close_frame; /* if 0, unknown; if non-0, known */
622 add_pol_handle(policy_hnd, pinfo->fd->num, pol, value);
625 /* Store a text string with a policy handle */
627 void dcerpc_smb_store_pol_name(e_ctx_hnd *policy_hnd, packet_info *pinfo,
630 pol_hash_value *value;
634 * By the time the first pass is done, the policy handle database
635 * has been completely constructed. If we've already seen this
636 * frame, there's nothing to do.
638 if (pinfo->fd->flags.visited)
641 if (is_null_pol(policy_hnd))
644 /* Look up existing value */
645 pol = find_pol_handle(policy_hnd, pinfo->fd->num, &value);
649 * This is the first pass; update the existing
650 * value as appropriate.
652 if (pol->name && name) {
653 #ifdef DEBUG_HASH_COLL
654 if (strcmp(pol->name, name) != 0)
655 g_warning("dcerpc_smb: pol_hash name collision %s/%s\n", value->name, name);
660 pol->name = strdup(name);
665 /* Create a new value */
667 pol = g_mem_chunk_alloc(pol_value_chunk);
670 pol->close_frame = 0;
671 pol->first_frame = pinfo->fd->num;
675 pol->name = strdup(name);
677 pol->name = strdup("<UNKNOWN>");
679 add_pol_handle(policy_hnd, pinfo->fd->num, pol, value);
683 * Retrieve a policy handle.
685 * XXX - should this get an "is_close" argument, and match even closed
686 * policy handles if the call is a close, so we can handle retransmitted
690 gboolean dcerpc_smb_fetch_pol(e_ctx_hnd *policy_hnd, char **name,
691 guint32 *open_frame, guint32 *close_frame,
694 pol_hash_value *value;
697 /* Prevent uninitialised return vars */
708 /* Look up existing value */
709 pol = find_pol_handle(policy_hnd, cur_frame, &value);
716 *open_frame = pol->open_frame;
719 *close_frame = pol->close_frame;
725 /* Iterator to free a policy handle key/value pair, and all
726 the policy handle values to which the hash table value
729 static void free_pol_keyvalue(gpointer key _U_, gpointer value_arg,
730 gpointer user_data _U_)
732 pol_hash_value *value = (pol_hash_value *)value_arg;
737 for (pol = value->list; pol != NULL; pol = pol->next) {
743 /* Initialise policy handle hash */
745 static void init_pol_hash(void)
747 /* Initialise memory chunks */
749 if (pol_hash_key_chunk)
750 g_mem_chunk_destroy(pol_hash_key_chunk);
752 pol_hash_key_chunk = g_mem_chunk_new(
753 "Policy handle hash keys", sizeof(pol_hash_key),
754 POL_HASH_INIT_COUNT * sizeof(pol_hash_key), G_ALLOC_ONLY);
757 g_mem_chunk_destroy(pol_value_chunk);
759 pol_value_chunk = g_mem_chunk_new(
760 "Policy handle values", sizeof(pol_value),
761 POL_HASH_INIT_COUNT * sizeof(pol_value), G_ALLOC_ONLY);
763 if (pol_hash_value_chunk)
764 g_mem_chunk_destroy(pol_hash_value_chunk);
766 pol_hash_value_chunk = g_mem_chunk_new(
767 "Policy handle hash values", sizeof(pol_hash_value),
768 POL_HASH_INIT_COUNT * sizeof(pol_hash_value), G_ALLOC_ONLY);
770 /* Initialise hash table */
773 g_hash_table_foreach(pol_hash, free_pol_keyvalue, NULL);
774 g_hash_table_destroy(pol_hash);
777 pol_hash = g_hash_table_new(pol_hash_fn, pol_hash_compare);
780 /* Dissect a NT status code */
783 dissect_ntstatus(tvbuff_t *tvb, gint offset, packet_info *pinfo,
784 proto_tree *tree, guint8 *drep,
785 int hfindex, guint32 *pdata)
789 offset = dissect_ndr_uint32(tvb, offset, pinfo, tree, drep,
792 if (status != 0 && check_col(pinfo->cinfo, COL_INFO))
793 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
794 val_to_str(status, NT_errors,
795 "Unknown error 0x%08x"));
802 /* Dissect a DOS status code */
805 dissect_doserror(tvbuff_t *tvb, gint offset, packet_info *pinfo,
806 proto_tree *tree, guint8 *drep,
807 int hfindex, guint32 *pdata)
811 offset = dissect_ndr_uint32(tvb, offset, pinfo, tree, drep,
814 if (status != 0 && check_col(pinfo->cinfo, COL_INFO))
815 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
816 val_to_str(status, DOS_errors,
817 "Unknown error 0x%08x"));
824 /* Dissect a NT policy handle */
826 static int hf_nt_policy_open_frame = -1;
827 static int hf_nt_policy_close_frame = -1;
829 static gint ett_nt_policy_hnd = -1;
832 dissect_nt_policy_hnd(tvbuff_t *tvb, gint offset, packet_info *pinfo,
833 proto_tree *tree, guint8 *drep, int hfindex,
834 e_ctx_hnd *pdata, proto_item **pitem,
835 gboolean is_open, gboolean is_close)
840 guint32 open_frame = 0, close_frame = 0;
842 int old_offset = offset;
845 di=pinfo->private_data;
846 if(di->conformant_run){
848 * just a run to handle conformant arrays, no scalars to
849 * dissect - and "dissect_ndr_ctx_hnd()" won't return
850 * a handle, so we can't do the hashing stuff in any
856 /* Add to proto tree */
858 item = proto_tree_add_text(tree, tvb, offset, sizeof(e_ctx_hnd),
861 subtree = proto_item_add_subtree(item, ett_nt_policy_hnd);
863 offset = dissect_ndr_ctx_hnd(tvb, offset, pinfo, subtree, drep,
867 * Create a new entry for this handle if it's not a null handle
868 * and no entry already exists, and, in any case, set the
869 * open, close, first, and last frame information as appropriate.
871 dcerpc_smb_store_pol_pkts(&hnd, pinfo, is_open, is_close);
873 /* Insert open/close/name information if known */
875 if (dcerpc_smb_fetch_pol(&hnd, &name, &open_frame, &close_frame,
880 item=proto_tree_add_uint(
881 subtree, hf_nt_policy_open_frame, tvb,
882 old_offset, sizeof(e_ctx_hnd), open_frame);
883 PROTO_ITEM_SET_GENERATED(item);
887 item=proto_tree_add_uint(
888 subtree, hf_nt_policy_close_frame, tvb,
889 old_offset, sizeof(e_ctx_hnd), close_frame);
890 PROTO_ITEM_SET_GENERATED(item);
894 * Don't append the handle name if pitem is null; that's
895 * an indication that our caller will do so, as we're
896 * supplying a pointer to the item so that they can do
899 if (name != NULL && pitem == NULL)
900 proto_item_append_text(item, ": %s", name);
912 /* Some helper routines to dissect a range of uint8 characters. I don't
913 think these are "official" NDR representations and are probably specific
914 to NT so for the moment they're put here instead of in packet-dcerpc.c
915 and packet-dcerpc-ndr.c. */
918 dissect_dcerpc_uint8s(tvbuff_t *tvb, gint offset, packet_info *pinfo _U_,
919 proto_tree *tree, guint8 *drep, int hfindex,
920 int length, const guint8 **pdata)
924 data = (const guint8 *)tvb_get_ptr(tvb, offset, length);
927 proto_tree_add_item (tree, hfindex, tvb, offset, length, (drep[0] & 0x10));
933 return offset + length;
937 dissect_ndr_uint8s(tvbuff_t *tvb, gint offset, packet_info *pinfo,
938 proto_tree *tree, guint8 *drep,
939 int hfindex, int length, const guint8 **pdata)
943 di=pinfo->private_data;
944 if(di->conformant_run){
945 /* just a run to handle conformant arrays, no scalars to dissect */
949 /* no alignment needed */
950 return dissect_dcerpc_uint8s(tvb, offset, pinfo,
951 tree, drep, hfindex, length, pdata);
955 dissect_dcerpc_uint16s(tvbuff_t *tvb, gint offset, packet_info *pinfo _U_,
956 proto_tree *tree, guint8 *drep, int hfindex,
960 proto_tree_add_item (tree, hfindex, tvb, offset, length * 2, (drep[0] & 0x10));
963 return offset + length * 2;
967 dissect_ndr_uint16s(tvbuff_t *tvb, gint offset, packet_info *pinfo,
968 proto_tree *tree, guint8 *drep,
969 int hfindex, int length)
973 di=pinfo->private_data;
974 if(di->conformant_run){
975 /* just a run to handle conformant arrays, no scalars to dissect */
982 return dissect_dcerpc_uint16s(tvb, offset, pinfo,
983 tree, drep, hfindex, length);
987 * Helper routines for dissecting NDR strings
989 void cb_wstr_postprocess(packet_info *pinfo, proto_tree *tree _U_,
990 proto_item *item, tvbuff_t *tvb,
991 int start_offset, int end_offset,
994 gint options = GPOINTER_TO_INT(callback_args);
995 gint levels = CB_STR_ITEM_LEVELS(options);
998 /* Align start_offset on 4-byte boundary. */
1000 if (start_offset % 4)
1001 start_offset += 4 - (start_offset % 4);
1003 /* Get string value */
1005 if ((end_offset - start_offset) <= 12)
1006 return; /* XXX: Use unistr2 dissector instead? */
1009 * XXX - need to handle non-printable characters here.
1011 * XXX - this is typically called after the string has already
1012 * been fetched and processed by some other routine; is there
1013 * some way we can get that string, rather than duplicating the
1014 * efforts of that routine?
1016 s = tvb_get_ephemeral_faked_unicode(
1017 tvb, start_offset + 12, (end_offset - start_offset - 12) / 2,
1020 /* Append string to COL_INFO */
1022 if (options & CB_STR_COL_INFO) {
1023 if (check_col(pinfo->cinfo, COL_INFO))
1024 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", s);
1027 /* Append string to upper-level proto_items */
1029 if (levels > 0 && item && s && s[0]) {
1030 proto_item_append_text(item, ": %s", s);
1031 item = item->parent;
1034 proto_item_append_text(item, ": %s", s);
1035 item = item->parent;
1037 while (levels > 0) {
1038 proto_item_append_text(item, " %s", s);
1039 item = item->parent;
1045 /* Save string to dcv->private_data */
1047 if (options & CB_STR_SAVE) {
1048 dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
1049 dcerpc_call_value *dcv = (dcerpc_call_value *)di->call_data;
1051 /* FIXME EPHEMERAL need to get rid of the g_strdup() and later g_free() */
1052 dcv->private_data = g_strdup(s);
1056 void cb_str_postprocess(packet_info *pinfo, proto_tree *tree _U_,
1057 proto_item *item, tvbuff_t *tvb,
1058 int start_offset, int end_offset,
1059 void *callback_args)
1061 gint options = GPOINTER_TO_INT(callback_args);
1062 gint levels = CB_STR_ITEM_LEVELS(options);
1065 /* Align start_offset on 4-byte boundary. */
1067 if (start_offset % 4)
1068 start_offset += 4 - (start_offset % 4);
1070 /* Get string value */
1072 if ((end_offset - start_offset) <= 12)
1073 return; /* XXX: Use unistr2 dissector instead? */
1076 * XXX - need to handle non-printable characters here.
1078 * XXX - this is typically called after the string has already
1079 * been fetched and processed by some other routine; is there
1080 * some way we can get that string, rather than duplicating the
1081 * efforts of that routine?
1083 s = tvb_get_ephemeral_string(
1084 tvb, start_offset + 12, (end_offset - start_offset - 12) );
1086 /* Append string to COL_INFO */
1088 if (options & CB_STR_COL_INFO) {
1089 if (check_col(pinfo->cinfo, COL_INFO))
1090 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", s);
1093 /* Append string to upper-level proto_items */
1095 if (levels > 0 && item && s && s[0]) {
1096 proto_item_append_text(item, ": %s", s);
1097 item = item->parent;
1100 proto_item_append_text(item, ": %s", s);
1101 item = item->parent;
1103 while (levels > 0) {
1104 proto_item_append_text(item, " %s", s);
1105 item = item->parent;
1111 /* Save string to dcv->private_data */
1113 if (options & CB_STR_SAVE) {
1114 dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
1115 dcerpc_call_value *dcv = (dcerpc_call_value *)di->call_data;
1117 dcv->private_data = g_strdup(s);
1121 /* Dissect a pointer to a NDR string and append the string value to the
1124 int dissect_ndr_str_pointer_item(tvbuff_t *tvb, gint offset,
1125 packet_info *pinfo, proto_tree *tree,
1126 guint8 *drep, int type, const char *text,
1127 int hf_index, int levels)
1129 return dissect_ndr_pointer_cb(
1130 tvb, offset, pinfo, tree, drep,
1131 dissect_ndr_wchar_cvstring, type, text, hf_index,
1132 cb_wstr_postprocess, GINT_TO_POINTER(levels + 1));
1135 /* SID dissection routines */
1137 static int hf_nt_count = -1;
1138 static int hf_nt_domain_sid = -1;
1141 dissect_ndr_nt_SID(tvbuff_t *tvb, int offset, packet_info *pinfo,
1142 proto_tree *tree, guint8 *drep)
1144 dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
1145 dcerpc_call_value *dcv = (dcerpc_call_value *)di->call_data;
1149 if(di->hf_index!=-1){
1150 name=proto_registrar_get_name(di->hf_index);
1154 if(di->conformant_run){
1155 /* just a run to handle conformant arrays, no scalars to dissect */
1159 /* the SID contains a conformant array, first we must eat
1160 the 4-byte max_count before we can hand it off */
1162 offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
1165 offset = dissect_nt_sid(tvb, offset, tree, name, &sid_str,
1168 /* dcv can be null, for example when this ndr structure is embedded
1169 * inside non-dcerpc pdus, i.e. kerberos PAC structure
1172 dcv->private_data = sid_str;
1178 /* same as dissect_ndr_nt_SID() but takes the same options as counted strings
1179 do to prettify the dissect pane and the COL_INFO summary line
1182 dissect_ndr_nt_SID_with_options(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree, guint8 *drep, guint32 options)
1184 dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
1185 dcerpc_call_value *dcv = (dcerpc_call_value *)di->call_data;
1186 gint levels = CB_STR_ITEM_LEVELS(options);
1187 offset=dissect_ndr_nt_SID(tvb, offset, pinfo, tree, drep);
1189 if(dcv && dcv->private_data){
1190 char *s=dcv->private_data;
1191 proto_item *item=(proto_item *)tree;
1193 if ((options & CB_STR_COL_INFO)&&(!di->conformant_run)) {
1194 /* kludge, ugly, but this is called twice for all
1195 dcerpc interfaces due to how we chase pointers
1196 and putting the sid twice on the summary line
1198 Real solution would be to block updates to col_info
1199 while we just do a conformance run, this might
1200 have sideeffects so it needs some more thoughts first.
1202 if (check_col(pinfo->cinfo, COL_INFO))
1203 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", s);
1206 /* Append string to upper-level proto_items */
1208 if (levels > 0 && item && s && s[0]) {
1209 proto_item_append_text(item, ": %s", s);
1210 item = item->parent;
1213 proto_item_append_text(item, ": %s", s);
1214 item = item->parent;
1216 while (levels > 0) {
1217 proto_item_append_text(item, " %s", s);
1218 item = item->parent;
1229 dissect_ndr_nt_SID_hf_through_ptr(tvbuff_t *tvb, int offset, packet_info *pinfo,
1230 proto_tree *tree, guint8 *drep)
1232 offset = dissect_ndr_nt_SID(tvb, offset, pinfo, tree, drep);
1237 static gint ett_nt_sid_pointer = -1;
1240 dissect_ndr_nt_PSID(tvbuff_t *tvb, int offset,
1241 packet_info *pinfo, proto_tree *parent_tree,
1244 proto_item *item=NULL;
1245 proto_tree *tree=NULL;
1246 int old_offset=offset;
1249 item = proto_tree_add_text(parent_tree, tvb, offset, -1,
1251 tree = proto_item_add_subtree(item, ett_nt_sid_pointer);
1254 offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
1255 dissect_ndr_nt_SID_hf_through_ptr, NDR_POINTER_UNIQUE,
1256 "SID pointer", hf_nt_domain_sid);
1258 proto_item_set_len(item, offset-old_offset);
1262 static const true_false_string tfs_nt_acb_disabled = {
1263 "Account is DISABLED",
1264 "Account is NOT disabled"
1266 static const true_false_string tfs_nt_acb_homedirreq = {
1267 "Homedir is REQUIRED",
1268 "Homedir is NOT required"
1270 static const true_false_string tfs_nt_acb_pwnotreq = {
1271 "Password is NOT required",
1272 "Password is REQUIRED"
1274 static const true_false_string tfs_nt_acb_tempdup = {
1275 "This is a TEMPORARY DUPLICATE account",
1276 "This is NOT a temporary duplicate account"
1278 static const true_false_string tfs_nt_acb_normal = {
1279 "This is a NORMAL USER account",
1280 "This is NOT a normal user account"
1282 static const true_false_string tfs_nt_acb_mns = {
1283 "This is a MNS account",
1284 "This is NOT a mns account"
1286 static const true_false_string tfs_nt_acb_domtrust = {
1287 "This is a DOMAIN TRUST account",
1288 "This is NOT a domain trust account"
1290 static const true_false_string tfs_nt_acb_wstrust = {
1291 "This is a WORKSTATION TRUST account",
1292 "This is NOT a workstation trust account"
1294 static const true_false_string tfs_nt_acb_svrtrust = {
1295 "This is a SERVER TRUST account",
1296 "This is NOT a server trust account"
1298 static const true_false_string tfs_nt_acb_pwnoexp = {
1299 "Passwords does NOT expire",
1300 "Password will EXPIRE"
1302 static const true_false_string tfs_nt_acb_autolock = {
1303 "This account has been AUTO LOCKED",
1304 "This account has NOT been auto locked"
1307 static gint ett_nt_acct_ctrl = -1;
1309 static int hf_nt_acct_ctrl = -1;
1310 static int hf_nt_acb_disabled = -1;
1311 static int hf_nt_acb_homedirreq = -1;
1312 static int hf_nt_acb_pwnotreq = -1;
1313 static int hf_nt_acb_tempdup = -1;
1314 static int hf_nt_acb_normal = -1;
1315 static int hf_nt_acb_mns = -1;
1316 static int hf_nt_acb_domtrust = -1;
1317 static int hf_nt_acb_wstrust = -1;
1318 static int hf_nt_acb_svrtrust = -1;
1319 static int hf_nt_acb_pwnoexp = -1;
1320 static int hf_nt_acb_autolock = -1;
1323 dissect_ndr_nt_acct_ctrl(tvbuff_t *tvb, int offset, packet_info *pinfo,
1324 proto_tree *parent_tree, guint8 *drep)
1327 proto_item *item = NULL;
1328 proto_tree *tree = NULL;
1330 offset=dissect_ndr_uint32(tvb, offset, pinfo, NULL, drep,
1331 hf_nt_acct_ctrl, &mask);
1334 item = proto_tree_add_uint(parent_tree, hf_nt_acct_ctrl,
1335 tvb, offset-4, 4, mask);
1336 tree = proto_item_add_subtree(item, ett_nt_acct_ctrl);
1339 proto_tree_add_boolean(tree, hf_nt_acb_autolock,
1340 tvb, offset-4, 4, mask);
1341 proto_tree_add_boolean(tree, hf_nt_acb_pwnoexp,
1342 tvb, offset-4, 4, mask);
1343 proto_tree_add_boolean(tree, hf_nt_acb_svrtrust,
1344 tvb, offset-4, 4, mask);
1345 proto_tree_add_boolean(tree, hf_nt_acb_wstrust,
1346 tvb, offset-4, 4, mask);
1347 proto_tree_add_boolean(tree, hf_nt_acb_domtrust,
1348 tvb, offset-4, 4, mask);
1349 proto_tree_add_boolean(tree, hf_nt_acb_mns,
1350 tvb, offset-4, 4, mask);
1351 proto_tree_add_boolean(tree, hf_nt_acb_normal,
1352 tvb, offset-4, 4, mask);
1353 proto_tree_add_boolean(tree, hf_nt_acb_tempdup,
1354 tvb, offset-4, 4, mask);
1355 proto_tree_add_boolean(tree, hf_nt_acb_pwnotreq,
1356 tvb, offset-4, 4, mask);
1357 proto_tree_add_boolean(tree, hf_nt_acb_homedirreq,
1358 tvb, offset-4, 4, mask);
1359 proto_tree_add_boolean(tree, hf_nt_acb_disabled,
1360 tvb, offset-4, 4, mask);
1365 static int hf_logonhours_unknown_char;
1368 dissect_LOGON_HOURS_entry(tvbuff_t *tvb, int offset,
1369 packet_info *pinfo, proto_tree *tree,
1372 offset = dissect_ndr_uint8(tvb, offset, pinfo, tree, drep,
1373 hf_logonhours_unknown_char, NULL);
1377 static gint ett_nt_logon_hours_hours = -1;
1380 dissect_LOGON_HOURS_hours(tvbuff_t *tvb, int offset,
1381 packet_info *pinfo, proto_tree *parent_tree,
1384 proto_item *item=NULL;
1385 proto_tree *tree=NULL;
1386 int old_offset=offset;
1389 item = proto_tree_add_text(parent_tree, tvb, offset, -1,
1391 tree = proto_item_add_subtree(item, ett_nt_logon_hours_hours);
1394 offset = dissect_ndr_ucvarray(tvb, offset, pinfo, tree, drep,
1395 dissect_LOGON_HOURS_entry);
1397 proto_item_set_len(item, offset-old_offset);
1403 static gint ett_nt_logon_hours = -1;
1404 static int hf_logonhours_divisions = -1;
1407 dissect_ndr_nt_LOGON_HOURS(tvbuff_t *tvb, int offset,
1408 packet_info *pinfo, proto_tree *parent_tree,
1411 proto_item *item=NULL;
1412 proto_tree *tree=NULL;
1413 int old_offset=offset;
1415 ALIGN_TO_4_BYTES; /* strcture starts with short, but is aligned for longs */
1418 item = proto_tree_add_text(parent_tree, tvb, offset, -1,
1420 tree = proto_item_add_subtree(item, ett_nt_logon_hours);
1423 offset = dissect_ndr_uint16(tvb, offset, pinfo, tree, drep,
1424 hf_logonhours_divisions, NULL);
1425 /* XXX - is this a bitmask like the "logon hours" field in the
1426 Remote API call "NetUserGetInfo()" with an information level
1428 offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
1429 dissect_LOGON_HOURS_hours, NDR_POINTER_UNIQUE,
1432 proto_item_set_len(item, offset-old_offset);
1437 dissect_ndr_nt_PSID_no_hf(tvbuff_t *tvb, int offset,
1438 packet_info *pinfo, proto_tree *parent_tree,
1441 offset=dissect_ndr_nt_PSID(tvb, offset, pinfo, parent_tree, drep);
1446 dissect_ndr_nt_PSID_ARRAY_sids (tvbuff_t *tvb, int offset,
1447 packet_info *pinfo, proto_tree *tree,
1450 offset = dissect_ndr_ucarray(tvb, offset, pinfo, tree, drep,
1451 dissect_ndr_nt_PSID_no_hf);
1456 static gint ett_nt_sid_array = -1;
1459 dissect_ndr_nt_PSID_ARRAY(tvbuff_t *tvb, int offset,
1460 packet_info *pinfo, proto_tree *parent_tree,
1464 proto_item *item=NULL;
1465 proto_tree *tree=NULL;
1466 int old_offset=offset;
1469 item = proto_tree_add_text(parent_tree, tvb, offset, -1,
1471 tree = proto_item_add_subtree(item, ett_nt_sid_array);
1474 offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
1475 hf_nt_count, &count);
1476 offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
1477 dissect_ndr_nt_PSID_ARRAY_sids, NDR_POINTER_UNIQUE,
1480 proto_item_set_len(item, offset-old_offset);
1484 static gint ett_nt_sid_and_attributes = -1;
1485 static int hf_nt_attrib = -1;
1488 dissect_ndr_nt_SID_AND_ATTRIBUTES(tvbuff_t *tvb, int offset,
1489 packet_info *pinfo, proto_tree *parent_tree,
1492 proto_item *item=NULL;
1493 proto_tree *tree=NULL;
1496 item = proto_tree_add_text(parent_tree, tvb, offset, 0,
1497 "SID_AND_ATTRIBUTES:");
1498 tree = proto_item_add_subtree(item, ett_nt_sid_and_attributes);
1501 offset = dissect_ndr_nt_PSID(tvb, offset, pinfo, tree, drep);
1503 offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
1504 hf_nt_attrib, NULL);
1509 static gint ett_nt_sid_and_attributes_array = -1;
1512 dissect_ndr_nt_SID_AND_ATTRIBUTES_ARRAY(tvbuff_t *tvb, int offset,
1513 packet_info *pinfo, proto_tree *parent_tree,
1516 proto_item *item=NULL;
1517 proto_tree *tree=NULL;
1518 int old_offset=offset;
1521 item = proto_tree_add_text(parent_tree, tvb, offset, 0,
1522 "SID_AND_ATTRIBUTES array:");
1523 tree = proto_item_add_subtree(item, ett_nt_sid_and_attributes_array);
1526 /*offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
1527 hf_samr_count, &count); */
1528 offset = dissect_ndr_ucarray(tvb, offset, pinfo, tree, drep,
1529 dissect_ndr_nt_SID_AND_ATTRIBUTES);
1531 proto_item_set_len(item, offset-old_offset);
1536 * Register ett/hf values and perform DCERPC over SMB specific
1539 void dcerpc_smb_init(int proto_dcerpc)
1541 static hf_register_info hf[] = {
1543 /* String handling */
1546 { "Size", "nt.str.size", FT_UINT16, BASE_DEC,
1547 NULL, 0x0, "Size of string in short integers",
1551 { "Length", "nt.str.len", FT_UINT16, BASE_DEC,
1552 NULL, 0x0, "Length of string in short integers",
1557 { "GUID", "nt.guid", FT_STRING, BASE_NONE,
1558 NULL, 0x0, "GUID (uuid for groups?)", HFILL }},
1560 /* Policy handles */
1562 { &hf_nt_policy_open_frame,
1563 { "Frame handle opened", "dcerpc.nt.open_frame",
1564 FT_FRAMENUM, BASE_NONE, NULL, 0x0,
1565 "Frame handle opened", HFILL }},
1567 { &hf_nt_policy_close_frame,
1568 { "Frame handle closed", "dcerpc.nt.close_frame",
1569 FT_FRAMENUM, BASE_NONE, NULL, 0x0,
1570 "Frame handle closed", HFILL }},
1575 { "Acct Ctrl", "nt.acct_ctrl", FT_UINT32, BASE_HEX,
1576 NULL, 0x0, "Acct CTRL", HFILL }},
1578 { &hf_nt_acb_disabled,
1579 { "", "nt.acb.disabled", FT_BOOLEAN, 32,
1580 TFS(&tfs_nt_acb_disabled), 0x0001,
1581 "If this account is enabled or disabled", HFILL }},
1583 { &hf_nt_acb_homedirreq,
1584 { "", "nt.acb.homedirreq", FT_BOOLEAN, 32,
1585 TFS(&tfs_nt_acb_homedirreq), 0x0002,
1586 "Is hom,edirs required for this account?", HFILL }},
1588 { &hf_nt_acb_pwnotreq,
1589 { "", "nt.acb.pwnotreq", FT_BOOLEAN, 32,
1590 TFS(&tfs_nt_acb_pwnotreq), 0x0004,
1591 "If a password is required for this account?", HFILL }},
1593 { &hf_nt_acb_tempdup,
1594 { "", "nt.acb.tempdup", FT_BOOLEAN, 32,
1595 TFS(&tfs_nt_acb_tempdup), 0x0008,
1596 "If this is a temporary duplicate account", HFILL }},
1598 { &hf_nt_acb_normal,
1599 { "", "nt.acb.normal", FT_BOOLEAN, 32,
1600 TFS(&tfs_nt_acb_normal), 0x0010,
1601 "If this is a normal user account", HFILL }},
1604 { "", "nt.acb.mns", FT_BOOLEAN, 32,
1605 TFS(&tfs_nt_acb_mns), 0x0020,
1606 "MNS logon user account", HFILL }},
1608 { &hf_nt_acb_domtrust,
1609 { "", "nt.acb.domtrust", FT_BOOLEAN, 32,
1610 TFS(&tfs_nt_acb_domtrust), 0x0040,
1611 "Interdomain trust account", HFILL }},
1613 { &hf_nt_acb_wstrust,
1614 { "", "nt.acb.wstrust", FT_BOOLEAN, 32,
1615 TFS(&tfs_nt_acb_wstrust), 0x0080,
1616 "Workstation trust account", HFILL }},
1618 { &hf_nt_acb_svrtrust,
1619 { "", "nt.acb.svrtrust", FT_BOOLEAN, 32,
1620 TFS(&tfs_nt_acb_svrtrust), 0x0100,
1621 "Server trust account", HFILL }},
1623 { &hf_nt_acb_pwnoexp,
1624 { "", "nt.acb.pwnoexp", FT_BOOLEAN, 32,
1625 TFS(&tfs_nt_acb_pwnoexp), 0x0200,
1626 "If this account expires or not", HFILL }},
1628 { &hf_nt_acb_autolock,
1629 { "", "nt.acb.autolock", FT_BOOLEAN, 32,
1630 TFS(&tfs_nt_acb_autolock), 0x0400,
1631 "If this account has been autolocked", HFILL }},
1635 { &hf_nt_domain_sid,
1636 { "Domain SID", "nt.domain_sid",
1637 FT_STRING, BASE_NONE, NULL, 0x0,
1638 "The Domain SID", HFILL }},
1641 { "Count", "nt.count",
1642 FT_UINT32, BASE_DEC, NULL, 0x0,
1643 "Number of elements in following array", HFILL }},
1647 { &hf_logonhours_divisions,
1648 { "Divisions", "logonhours.divisions",
1649 FT_UINT16, BASE_DEC, NULL, 0,
1650 "Number of divisions for LOGON_HOURS", HFILL }},
1652 { &hf_logonhours_unknown_char,
1653 { "Unknown char", "nt.unknown.char",
1654 FT_UINT8, BASE_HEX, NULL, 0x0,
1655 "Unknown char. If you know what this is, contact "
1656 "ethereal developers.", HFILL }},
1661 { "Attributes", "nt.attr",
1662 FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL }},
1665 static gint *ett[] = {
1666 &ett_nt_unicode_string,
1667 &ett_nt_counted_string,
1668 &ett_nt_counted_byte_array,
1670 &ett_nt_sid_pointer,
1672 &ett_nt_logon_hours,
1673 &ett_nt_logon_hours_hours,
1675 &ett_nt_sid_and_attributes_array,
1676 &ett_nt_sid_and_attributes,
1677 &ett_nt_counted_ascii_string,
1680 /* Register ett's and hf's */
1682 proto_register_subtree_array(ett, array_length(ett));
1683 proto_register_field_array(proto_dcerpc, hf, array_length(hf));
1685 /* Initialise policy handle hash */