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.80 2004/05/19 04:52:31 tpot 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.
43 * Used by several dissectors.
45 const value_string platform_id_vals[] = {
48 { 500, "Windows NT" },
54 /* Parse some common RPC structures */
56 gint ett_nt_unicode_string = -1; /* FIXME: make static */
58 /* Dissect a counted string as a callback to dissect_ndr_pointer_cb() */
60 static int hf_nt_cs_len = -1;
61 static int hf_nt_cs_size = -1;
64 dissect_ndr_counted_string_cb(tvbuff_t *tvb, int offset,
65 packet_info *pinfo, proto_tree *tree,
66 guint8 *drep, int hf_index,
67 dcerpc_callback_fnct_t *callback,
70 dcerpc_info *di = pinfo->private_data;
73 /* Structure starts with short, but is aligned for longs */
77 if (di->conformant_run)
84 [size_is(size/2), length_is(len/2), ptr] unsigned short *string;
89 offset = dissect_ndr_uint16(tvb, offset, pinfo, tree, drep,
92 offset = dissect_ndr_uint16(tvb, offset, pinfo, tree, drep,
93 hf_nt_cs_size, &size);
95 offset = dissect_ndr_pointer_cb(tvb, offset, pinfo, tree, drep,
96 dissect_ndr_wchar_cvstring, NDR_POINTER_UNIQUE,
97 "Character Array", hf_index, callback, callback_args);
102 static gint ett_nt_counted_string = -1;
105 dissect_ndr_counted_string_helper(tvbuff_t *tvb, int offset,
106 packet_info *pinfo, proto_tree *tree,
107 guint8 *drep, int hf_index, int levels,
108 gboolean add_subtree)
111 proto_tree *subtree = tree;
115 item = proto_tree_add_text(
116 tree, tvb, offset, 0,
117 proto_registrar_get_name(hf_index));
119 subtree = proto_item_add_subtree(item, ett_nt_counted_string);
123 * Add 2 levels, so that the string gets attached to the
124 * "Character Array" top-level item and to the top-level item
127 return dissect_ndr_counted_string_cb(
128 tvb, offset, pinfo, subtree, drep, hf_index,
129 cb_wstr_postprocess, GINT_TO_POINTER(2 + levels));
132 /* Dissect a counted string in-line. */
135 dissect_ndr_counted_string(tvbuff_t *tvb, int offset,
136 packet_info *pinfo, proto_tree *tree,
137 guint8 *drep, int hf_index, int levels)
139 return dissect_ndr_counted_string_helper(
140 tvb, offset, pinfo, tree, drep, hf_index, levels, TRUE);
143 /* Dissect a counted string as a callback to dissect_ndr_pointer().
144 This doesn't add a adds a proto item and subtreee for the string as
145 the pointer dissection already creates one. */
148 dissect_ndr_counted_string_ptr(tvbuff_t *tvb, int offset,
149 packet_info *pinfo, proto_tree *tree,
152 dcerpc_info *di = pinfo->private_data;
154 return dissect_ndr_counted_string_helper(
155 tvb, offset, pinfo, tree, drep, di->hf_index, 0, FALSE);
158 /* Dissect a counted byte_array as a callback to dissect_ndr_pointer_cb() */
160 static gint ett_nt_counted_byte_array = -1;
162 /* Dissect a counted byte array in-line. */
165 dissect_ndr_counted_byte_array_cb(tvbuff_t *tvb, int offset,
166 packet_info *pinfo, proto_tree *tree,
167 guint8 *drep, int hf_index,
168 dcerpc_callback_fnct_t *callback,
171 dcerpc_info *di = pinfo->private_data;
176 /* Structure starts with short, but is aligned for longs */
180 if (di->conformant_run)
183 item = proto_tree_add_text(tree, tvb, offset, 0,
184 proto_registrar_get_name(hf_index));
186 subtree = proto_item_add_subtree(item, ett_nt_counted_byte_array);
192 [size_is(size), length_is(len), ptr] unsigned char *string;
193 } WHATEVER_THIS_IS_CALLED;
197 offset = dissect_ndr_uint16(tvb, offset, pinfo, subtree, drep,
200 offset = dissect_ndr_uint16(tvb, offset, pinfo, subtree, drep,
201 hf_nt_cs_size, &size);
203 offset = dissect_ndr_pointer_cb(tvb, offset, pinfo, subtree, drep,
204 dissect_ndr_char_cvstring, NDR_POINTER_UNIQUE,
205 "Byte Array", hf_index, callback, callback_args);
211 dissect_ndr_counted_byte_array(tvbuff_t *tvb, int offset,
212 packet_info *pinfo, proto_tree *tree,
213 guint8 *drep, int hf_index)
215 return dissect_ndr_counted_byte_array_cb(
216 tvb, offset, pinfo, tree, drep, hf_index, NULL, NULL);
219 /* This function is used to dissect a DCERPC encoded 64 bit time value.
220 XXX it should be fixed both here and in dissect_smb_64bit_time so
221 it can handle both BIG and LITTLE endian encodings
224 dissect_ndr_nt_NTTIME (tvbuff_t *tvb, int offset,
225 packet_info *pinfo, proto_tree *tree,
226 guint8 *drep _U_, int hf_index)
230 di=pinfo->private_data;
231 if(di->conformant_run){
232 /*just a run to handle conformant arrays, nothing to dissect */
238 offset = dissect_smb_64bit_time(tvb, tree, offset, hf_index);
242 /* Define this symbol to display warnings about request/response and
243 policy handle hash table collisions. This happens when a packet with
244 the same conversation, smb fid and dcerpc call id occurs. I think this
245 is due to a bug in the dcerpc/smb fragment reassembly code. */
247 #undef DEBUG_HASH_COLL
250 * Policy handle hashing.
252 * We hash based on the policy handle value; the items in the hash table
253 * are lists of policy handle information about one or more policy
254 * handles with that value. We have multiple values in case a given
255 * policy handle is opened in frame N, closed in frame M, and re-opened
256 * in frame O, where N < M < O.
258 * XXX - we really should also use a DCE RPC conversation/session handle
259 * of some sort, in case two separate sessions have the same handle
260 * value. A transport-layer conversation might not be sufficient, as you
261 * might, for example, have multiple pipes in a single SMB connection,
262 * and you might have the same handle opened and closed separately on
265 * The policy handle information has "first frame" and "last frame"
266 * information; the entry should be used when dissecting a given frame
267 * only if that frame is within the interval [first frame,last frame].
268 * The list is sorted by "first frame".
270 * This doesn't handle the case of a handle being opened in frame N and
271 * re-opened in frame M, where N < M, with no intervening close, but I'm
272 * not sure anything can handle that if it's within the same DCE RPC
273 * session (if it's not, the conversation/session handle would fix that).
277 guint8 policy_hnd[20];
280 typedef struct pol_value {
281 struct pol_value *next; /* Next entry in hash bucket */
282 guint32 open_frame, close_frame; /* Frame numbers for open/close */
283 guint32 first_frame; /* First frame in which this instance was seen */
284 guint32 last_frame; /* Last frame in which this instance was seen */
285 char *name; /* Name of policy handle */
289 pol_value *list; /* List of policy handle entries */
292 #define POL_HASH_INIT_COUNT 100
294 static GHashTable *pol_hash;
295 static GMemChunk *pol_hash_key_chunk;
296 static GMemChunk *pol_value_chunk;
297 static GMemChunk *pol_hash_value_chunk;
301 static guint pol_hash_fn(gconstpointer k)
303 const pol_hash_key *key = (const pol_hash_key *)k;
305 /* Bytes 4-7 of the policy handle are a timestamp so should make a
306 reasonable hash value */
308 return key->policy_hnd[4] + (key->policy_hnd[5] << 8) +
309 (key->policy_hnd[6] << 16) + (key->policy_hnd[7] << 24);
312 /* Return true if a policy handle is all zeros */
314 static gboolean is_null_pol(e_ctx_hnd *policy_hnd)
316 static guint8 null_policy_hnd[20];
318 return memcmp(policy_hnd, null_policy_hnd, 20) == 0;
321 /* Hash compare function */
323 static gint pol_hash_compare(gconstpointer k1, gconstpointer k2)
325 const pol_hash_key *key1 = (const pol_hash_key *)k1;
326 const pol_hash_key *key2 = (const pol_hash_key *)k2;
328 return memcmp(key1->policy_hnd, key2->policy_hnd,
329 sizeof(key1->policy_hnd)) == 0;
333 * Look up the instance of a policy handle value in whose range of frames
334 * the specified frame falls.
336 static pol_value *find_pol_handle(e_ctx_hnd *policy_hnd, guint32 frame,
337 pol_hash_value **valuep)
342 memcpy(&key.policy_hnd, policy_hnd, sizeof(key.policy_hnd));
343 if ((*valuep = g_hash_table_lookup(pol_hash, &key))) {
345 * Look for the first value such that both:
347 * 1) the first frame in which it was seen is
348 * <= the specified frame;
350 * 2) the last frame in which it was seen is
351 * either unknown (meaning we haven't yet
352 * seen a close or another open of the
353 * same handle, which is assumed to imply
354 * an intervening close that wasn't captured)
355 * or is >= the specified frame.
357 * If there's more than one such frame, that's the
358 * case where a handle is opened in frame N and
359 * reopened in frame M, with no intervening close;
360 * there is no right answer for that, so the instance
361 * opened in frame N is as right as anything else.
363 for (pol = (*valuep)->list; pol != NULL; pol = pol->next) {
364 if (pol->first_frame <= frame &&
365 (pol->last_frame == 0 ||
366 pol->last_frame >= frame))
367 break; /* found one */
372 * The handle isn't in the hash table.
378 static void add_pol_handle(e_ctx_hnd *policy_hnd, guint32 frame,
379 pol_value *pol, pol_hash_value *value)
382 pol_value *polprev, *polnext;
386 * There's no hash value; create one, put the new
387 * value at the beginning of its policy handle list,
388 * and put the hash value in the policy handle hash
391 value = g_mem_chunk_alloc(pol_hash_value_chunk);
394 key = g_mem_chunk_alloc(pol_hash_key_chunk);
395 memcpy(&key->policy_hnd, policy_hnd, sizeof(key->policy_hnd));
396 g_hash_table_insert(pol_hash, key, value);
399 * Put the new value in the hash value's policy handle
400 * list so that it's sorted by the first frame in
403 * Search for the first entry whose first frame number
404 * is greater than the current frame number, if any.
406 for (polnext = value->list, polprev = NULL;
407 polnext != NULL && polnext->first_frame <= frame;
408 polprev = polnext, polnext = polnext->next)
412 * "polprev" points to the entry in the list after
413 * which we should put the new entry; if it's null,
414 * that means we should put it at the beginning of
423 * "polnext" points to the entry in the list before
424 * which we should put the new entry; if it's null,
425 * that means we should put it at the end of the list.
431 /* Store the open and close frame numbers of a policy handle */
433 void dcerpc_smb_store_pol_pkts(e_ctx_hnd *policy_hnd, packet_info *pinfo,
434 gboolean is_open, gboolean is_close)
436 pol_hash_value *value;
440 * By the time the first pass is done, the policy handle database
441 * has been completely constructed. If we've already seen this
442 * frame, there's nothing to do.
444 if (pinfo->fd->flags.visited)
447 if (is_null_pol(policy_hnd))
450 /* Look up existing value */
451 pol = find_pol_handle(policy_hnd, pinfo->fd->num, &value);
455 * Update the existing value as appropriate.
459 * This is an open; we assume that we missed
460 * a close of this handle, so we set its
461 * "last frame" value and act as if we didn't
464 * XXX - note that we might be called twice for
465 * the same operation (see "dissect_pipe_dcerpc()",
466 * which calls the DCE RPC dissector twice), so we
467 * must first check to see if this is a handle we
470 * We check whether this handle's "first frame"
471 * frame number is this frame and its "last frame
472 * is 0; if so, this is presumably a duplicate call,
473 * and we don't do an implicit close.
475 if (pol->first_frame == pinfo->fd->num &&
476 pol->last_frame == 0)
478 pol->last_frame = pinfo->fd->num;
482 pol->close_frame = pinfo->fd->num;
483 pol->last_frame = pinfo->fd->num;
489 /* Create a new value */
491 pol = g_mem_chunk_alloc(pol_value_chunk);
493 pol->open_frame = is_open ? pinfo->fd->num : 0;
494 pol->close_frame = is_close ? pinfo->fd->num : 0;
495 pol->first_frame = pinfo->fd->num;
496 pol->last_frame = pol->close_frame; /* if 0, unknown; if non-0, known */
500 add_pol_handle(policy_hnd, pinfo->fd->num, pol, value);
503 /* Store a text string with a policy handle */
505 void dcerpc_smb_store_pol_name(e_ctx_hnd *policy_hnd, packet_info *pinfo,
508 pol_hash_value *value;
512 * By the time the first pass is done, the policy handle database
513 * has been completely constructed. If we've already seen this
514 * frame, there's nothing to do.
516 if (pinfo->fd->flags.visited)
519 if (is_null_pol(policy_hnd))
522 /* Look up existing value */
523 pol = find_pol_handle(policy_hnd, pinfo->fd->num, &value);
527 * This is the first pass; update the existing
528 * value as appropriate.
530 if (pol->name && name) {
531 #ifdef DEBUG_HASH_COLL
532 if (strcmp(pol->name, name) != 0)
533 g_warning("dcerpc_smb: pol_hash name collision %s/%s\n", value->name, name);
538 pol->name = strdup(name);
543 /* Create a new value */
545 pol = g_mem_chunk_alloc(pol_value_chunk);
548 pol->close_frame = 0;
549 pol->first_frame = pinfo->fd->num;
553 pol->name = strdup(name);
555 pol->name = strdup("<UNKNOWN>");
557 add_pol_handle(policy_hnd, pinfo->fd->num, pol, value);
561 * Retrieve a policy handle.
563 * XXX - should this get an "is_close" argument, and match even closed
564 * policy handles if the call is a close, so we can handle retransmitted
568 gboolean dcerpc_smb_fetch_pol(e_ctx_hnd *policy_hnd, char **name,
569 guint32 *open_frame, guint32 *close_frame,
572 pol_hash_value *value;
575 /* Prevent uninitialised return vars */
586 /* Look up existing value */
587 pol = find_pol_handle(policy_hnd, cur_frame, &value);
594 *open_frame = pol->open_frame;
597 *close_frame = pol->close_frame;
603 /* Iterator to free a policy handle key/value pair, and all
604 the policy handle values to which the hash table value
607 static void free_pol_keyvalue(gpointer key _U_, gpointer value_arg,
608 gpointer user_data _U_)
610 pol_hash_value *value = (pol_hash_value *)value_arg;
615 for (pol = value->list; pol != NULL; pol = pol->next) {
621 /* Initialise policy handle hash */
623 static void init_pol_hash(void)
625 /* Initialise memory chunks */
627 if (pol_hash_key_chunk)
628 g_mem_chunk_destroy(pol_hash_key_chunk);
630 pol_hash_key_chunk = g_mem_chunk_new(
631 "Policy handle hash keys", sizeof(pol_hash_key),
632 POL_HASH_INIT_COUNT * sizeof(pol_hash_key), G_ALLOC_ONLY);
635 g_mem_chunk_destroy(pol_value_chunk);
637 pol_value_chunk = g_mem_chunk_new(
638 "Policy handle values", sizeof(pol_value),
639 POL_HASH_INIT_COUNT * sizeof(pol_value), G_ALLOC_ONLY);
641 if (pol_hash_value_chunk)
642 g_mem_chunk_destroy(pol_hash_value_chunk);
644 pol_hash_value_chunk = g_mem_chunk_new(
645 "Policy handle hash values", sizeof(pol_hash_value),
646 POL_HASH_INIT_COUNT * sizeof(pol_hash_value), G_ALLOC_ONLY);
648 /* Initialise hash table */
651 g_hash_table_foreach(pol_hash, free_pol_keyvalue, NULL);
652 g_hash_table_destroy(pol_hash);
655 pol_hash = g_hash_table_new(pol_hash_fn, pol_hash_compare);
658 /* Dissect a NT status code */
661 dissect_ntstatus(tvbuff_t *tvb, gint offset, packet_info *pinfo,
662 proto_tree *tree, guint8 *drep,
663 int hfindex, guint32 *pdata)
667 offset = dissect_ndr_uint32(tvb, offset, pinfo, tree, drep,
670 if (status != 0 && check_col(pinfo->cinfo, COL_INFO))
671 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
672 val_to_str(status, NT_errors,
673 "Unknown error 0x%08x"));
680 /* Dissect a DOS status code */
683 dissect_doserror(tvbuff_t *tvb, gint offset, packet_info *pinfo,
684 proto_tree *tree, guint8 *drep,
685 int hfindex, guint32 *pdata)
689 offset = dissect_ndr_uint32(tvb, offset, pinfo, tree, drep,
692 if (status != 0 && check_col(pinfo->cinfo, COL_INFO))
693 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
694 val_to_str(status, DOS_errors,
695 "Unknown error 0x%08x"));
702 /* Dissect a NT policy handle */
704 static int hf_nt_policy_open_frame = -1;
705 static int hf_nt_policy_close_frame = -1;
707 static gint ett_nt_policy_hnd = -1;
710 dissect_nt_policy_hnd(tvbuff_t *tvb, gint offset, packet_info *pinfo,
711 proto_tree *tree, guint8 *drep, int hfindex,
712 e_ctx_hnd *pdata, proto_item **pitem,
713 gboolean is_open, gboolean is_close)
718 guint32 open_frame = 0, close_frame = 0;
720 int old_offset = offset;
723 di=pinfo->private_data;
724 if(di->conformant_run){
726 * just a run to handle conformant arrays, no scalars to
727 * dissect - and "dissect_ndr_ctx_hnd()" won't return
728 * a handle, so we can't do the hashing stuff in any
734 /* Add to proto tree */
736 item = proto_tree_add_text(tree, tvb, offset, sizeof(e_ctx_hnd),
739 subtree = proto_item_add_subtree(item, ett_nt_policy_hnd);
741 offset = dissect_ndr_ctx_hnd(tvb, offset, pinfo, subtree, drep,
745 * Create a new entry for this handle if it's not a null handle
746 * and no entry already exists, and, in any case, set the
747 * open, close, first, and last frame information as appropriate.
749 dcerpc_smb_store_pol_pkts(&hnd, pinfo, is_open, is_close);
751 /* Insert open/close/name information if known */
753 if (dcerpc_smb_fetch_pol(&hnd, &name, &open_frame, &close_frame,
758 subtree, hf_nt_policy_open_frame, tvb,
759 old_offset, sizeof(e_ctx_hnd), open_frame);
763 subtree, hf_nt_policy_close_frame, tvb,
764 old_offset, sizeof(e_ctx_hnd), close_frame);
767 * Don't append the handle name if pitem is null; that's
768 * an indication that our caller will do so, as we're
769 * supplying a pointer to the item so that they can do
772 if (name != NULL && pitem == NULL)
773 proto_item_append_text(item, ": %s", name);
785 /* Some helper routines to dissect a range of uint8 characters. I don't
786 think these are "official" NDR representations and are probably specific
787 to NT so for the moment they're put here instead of in packet-dcerpc.c
788 and packet-dcerpc-ndr.c. */
791 dissect_dcerpc_uint8s(tvbuff_t *tvb, gint offset, packet_info *pinfo _U_,
792 proto_tree *tree, guint8 *drep, int hfindex,
793 int length, const guint8 **pdata)
797 data = (const guint8 *)tvb_get_ptr(tvb, offset, length);
800 proto_tree_add_item (tree, hfindex, tvb, offset, length, (drep[0] & 0x10));
806 return offset + length;
810 dissect_ndr_uint8s(tvbuff_t *tvb, gint offset, packet_info *pinfo,
811 proto_tree *tree, guint8 *drep,
812 int hfindex, int length, const guint8 **pdata)
816 di=pinfo->private_data;
817 if(di->conformant_run){
818 /* just a run to handle conformant arrays, no scalars to dissect */
822 /* no alignment needed */
823 return dissect_dcerpc_uint8s(tvb, offset, pinfo,
824 tree, drep, hfindex, length, pdata);
828 dissect_dcerpc_uint16s(tvbuff_t *tvb, gint offset, packet_info *pinfo _U_,
829 proto_tree *tree, guint8 *drep, int hfindex,
833 proto_tree_add_item (tree, hfindex, tvb, offset, length * 2, (drep[0] & 0x10));
836 return offset + length * 2;
840 dissect_ndr_uint16s(tvbuff_t *tvb, gint offset, packet_info *pinfo,
841 proto_tree *tree, guint8 *drep,
842 int hfindex, int length)
846 di=pinfo->private_data;
847 if(di->conformant_run){
848 /* just a run to handle conformant arrays, no scalars to dissect */
855 return dissect_dcerpc_uint16s(tvb, offset, pinfo,
856 tree, drep, hfindex, length);
860 * Helper routines for dissecting NDR strings
863 void cb_wstr_postprocess(packet_info *pinfo, proto_tree *tree _U_,
864 proto_item *item, tvbuff_t *tvb,
865 int start_offset, int end_offset,
868 gint options = GPOINTER_TO_INT(callback_args);
869 gint levels = CB_STR_ITEM_LEVELS(options);
872 /* Align start_offset on 4-byte boundary. */
874 if (start_offset % 4)
875 start_offset += 4 - (start_offset % 4);
877 /* Get string value */
879 if ((end_offset - start_offset) <= 12)
880 return; /* XXX: Use unistr2 dissector instead? */
883 * XXX - need to handle non-printable characters here.
885 * XXX - this is typically called after the string has already
886 * been fetched and processed by some other routine; is there
887 * some way we can get that string, rather than duplicating the
888 * efforts of that routine?
890 s = tvb_fake_unicode(
891 tvb, start_offset + 12, (end_offset - start_offset - 12) / 2,
894 /* Append string to COL_INFO */
896 if (options & CB_STR_COL_INFO) {
897 if (check_col(pinfo->cinfo, COL_INFO))
898 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", s);
901 /* Append string to upper-level proto_items */
903 if (levels > 0 && item && s && s[0]) {
904 proto_item_append_text(item, ": %s", s);
908 proto_item_append_text(item, ": %s", s);
912 proto_item_append_text(item, " %s", s);
919 /* Save string to dcv->private_data */
921 if (options & CB_STR_SAVE) {
922 dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
923 dcerpc_call_value *dcv = (dcerpc_call_value *)di->call_data;
925 dcv->private_data = g_strdup(s);
931 void cb_str_postprocess(packet_info *pinfo, proto_tree *tree _U_,
932 proto_item *item, tvbuff_t *tvb,
933 int start_offset, int end_offset,
936 gint options = GPOINTER_TO_INT(callback_args);
937 gint levels = CB_STR_ITEM_LEVELS(options);
940 /* Align start_offset on 4-byte boundary. */
942 if (start_offset % 4)
943 start_offset += 4 - (start_offset % 4);
945 /* Get string value */
947 if ((end_offset - start_offset) <= 12)
948 return; /* XXX: Use unistr2 dissector instead? */
951 * XXX - need to handle non-printable characters here.
953 * XXX - this is typically called after the string has already
954 * been fetched and processed by some other routine; is there
955 * some way we can get that string, rather than duplicating the
956 * efforts of that routine?
959 tvb, start_offset + 12, (end_offset - start_offset - 12) );
961 /* Append string to COL_INFO */
963 if (options & CB_STR_COL_INFO) {
964 if (check_col(pinfo->cinfo, COL_INFO))
965 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", s);
968 /* Append string to upper-level proto_items */
970 if (levels > 0 && item && s && s[0]) {
971 proto_item_append_text(item, ": %s", s);
975 proto_item_append_text(item, ": %s", s);
979 proto_item_append_text(item, " %s", s);
986 /* Save string to dcv->private_data */
988 if (options & CB_STR_SAVE) {
989 dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
990 dcerpc_call_value *dcv = (dcerpc_call_value *)di->call_data;
992 dcv->private_data = g_strdup(s);
998 /* Dissect a pointer to a NDR string and append the string value to the
1001 int dissect_ndr_str_pointer_item(tvbuff_t *tvb, gint offset,
1002 packet_info *pinfo, proto_tree *tree,
1003 guint8 *drep, int type, char *text,
1004 int hf_index, int levels)
1006 return dissect_ndr_pointer_cb(
1007 tvb, offset, pinfo, tree, drep,
1008 dissect_ndr_wchar_cvstring, type, text, hf_index,
1009 cb_wstr_postprocess, GINT_TO_POINTER(levels + 1));
1012 /* SID dissection routines */
1014 static int hf_nt_count = -1;
1015 static int hf_nt_domain_sid = -1;
1018 dissect_ndr_nt_SID(tvbuff_t *tvb, int offset, packet_info *pinfo,
1019 proto_tree *tree, guint8 *drep)
1021 dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
1022 dcerpc_call_value *dcv = (dcerpc_call_value *)di->call_data;
1026 if(di->hf_index!=-1){
1027 name=proto_registrar_get_name(di->hf_index);
1031 if(di->conformant_run){
1032 /* just a run to handle conformant arrays, no scalars to dissect */
1036 /* the SID contains a conformant array, first we must eat
1037 the 4-byte max_count before we can hand it off */
1039 offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
1042 offset = dissect_nt_sid(tvb, offset, tree, name, &sid_str,
1045 /* dcv can be null, for example when this ndr structure is embedded
1046 * inside non-dcerpc pdus, i.e. kerberos PAC structure
1049 dcv->private_data = sid_str;
1056 dissect_ndr_nt_SID_hf_through_ptr(tvbuff_t *tvb, int offset, packet_info *pinfo,
1057 proto_tree *tree, guint8 *drep)
1059 offset = dissect_ndr_nt_SID(tvb, offset, pinfo, tree, drep);
1064 static gint ett_nt_sid_pointer = -1;
1067 dissect_ndr_nt_PSID(tvbuff_t *tvb, int offset,
1068 packet_info *pinfo, proto_tree *parent_tree,
1071 proto_item *item=NULL;
1072 proto_tree *tree=NULL;
1073 int old_offset=offset;
1076 item = proto_tree_add_text(parent_tree, tvb, offset, -1,
1078 tree = proto_item_add_subtree(item, ett_nt_sid_pointer);
1081 offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
1082 dissect_ndr_nt_SID_hf_through_ptr, NDR_POINTER_UNIQUE,
1083 "SID pointer", hf_nt_domain_sid);
1085 proto_item_set_len(item, offset-old_offset);
1089 static const true_false_string tfs_nt_acb_disabled = {
1090 "Account is DISABLED",
1091 "Account is NOT disabled"
1093 static const true_false_string tfs_nt_acb_homedirreq = {
1094 "Homedir is REQUIRED",
1095 "Homedir is NOT required"
1097 static const true_false_string tfs_nt_acb_pwnotreq = {
1098 "Password is NOT required",
1099 "Password is REQUIRED"
1101 static const true_false_string tfs_nt_acb_tempdup = {
1102 "This is a TEMPORARY DUPLICATE account",
1103 "This is NOT a temporary duplicate account"
1105 static const true_false_string tfs_nt_acb_normal = {
1106 "This is a NORMAL USER account",
1107 "This is NOT a normal user account"
1109 static const true_false_string tfs_nt_acb_mns = {
1110 "This is a MNS account",
1111 "This is NOT a mns account"
1113 static const true_false_string tfs_nt_acb_domtrust = {
1114 "This is a DOMAIN TRUST account",
1115 "This is NOT a domain trust account"
1117 static const true_false_string tfs_nt_acb_wstrust = {
1118 "This is a WORKSTATION TRUST account",
1119 "This is NOT a workstation trust account"
1121 static const true_false_string tfs_nt_acb_svrtrust = {
1122 "This is a SERVER TRUST account",
1123 "This is NOT a server trust account"
1125 static const true_false_string tfs_nt_acb_pwnoexp = {
1126 "Passwords does NOT expire",
1127 "Password will EXPIRE"
1129 static const true_false_string tfs_nt_acb_autolock = {
1130 "This account has been AUTO LOCKED",
1131 "This account has NOT been auto locked"
1134 static gint ett_nt_acct_ctrl = -1;
1136 static int hf_nt_acct_ctrl = -1;
1137 static int hf_nt_acb_disabled = -1;
1138 static int hf_nt_acb_homedirreq = -1;
1139 static int hf_nt_acb_pwnotreq = -1;
1140 static int hf_nt_acb_tempdup = -1;
1141 static int hf_nt_acb_normal = -1;
1142 static int hf_nt_acb_mns = -1;
1143 static int hf_nt_acb_domtrust = -1;
1144 static int hf_nt_acb_wstrust = -1;
1145 static int hf_nt_acb_svrtrust = -1;
1146 static int hf_nt_acb_pwnoexp = -1;
1147 static int hf_nt_acb_autolock = -1;
1150 dissect_ndr_nt_acct_ctrl(tvbuff_t *tvb, int offset, packet_info *pinfo,
1151 proto_tree *parent_tree, guint8 *drep)
1154 proto_item *item = NULL;
1155 proto_tree *tree = NULL;
1157 offset=dissect_ndr_uint32(tvb, offset, pinfo, NULL, drep,
1158 hf_nt_acct_ctrl, &mask);
1161 item = proto_tree_add_uint(parent_tree, hf_nt_acct_ctrl,
1162 tvb, offset-4, 4, mask);
1163 tree = proto_item_add_subtree(item, ett_nt_acct_ctrl);
1166 proto_tree_add_boolean(tree, hf_nt_acb_autolock,
1167 tvb, offset-4, 4, mask);
1168 proto_tree_add_boolean(tree, hf_nt_acb_pwnoexp,
1169 tvb, offset-4, 4, mask);
1170 proto_tree_add_boolean(tree, hf_nt_acb_svrtrust,
1171 tvb, offset-4, 4, mask);
1172 proto_tree_add_boolean(tree, hf_nt_acb_wstrust,
1173 tvb, offset-4, 4, mask);
1174 proto_tree_add_boolean(tree, hf_nt_acb_domtrust,
1175 tvb, offset-4, 4, mask);
1176 proto_tree_add_boolean(tree, hf_nt_acb_mns,
1177 tvb, offset-4, 4, mask);
1178 proto_tree_add_boolean(tree, hf_nt_acb_normal,
1179 tvb, offset-4, 4, mask);
1180 proto_tree_add_boolean(tree, hf_nt_acb_tempdup,
1181 tvb, offset-4, 4, mask);
1182 proto_tree_add_boolean(tree, hf_nt_acb_pwnotreq,
1183 tvb, offset-4, 4, mask);
1184 proto_tree_add_boolean(tree, hf_nt_acb_homedirreq,
1185 tvb, offset-4, 4, mask);
1186 proto_tree_add_boolean(tree, hf_nt_acb_disabled,
1187 tvb, offset-4, 4, mask);
1192 static int hf_logonhours_unknown_char;
1195 dissect_LOGON_HOURS_entry(tvbuff_t *tvb, int offset,
1196 packet_info *pinfo, proto_tree *tree,
1199 offset = dissect_ndr_uint8(tvb, offset, pinfo, tree, drep,
1200 hf_logonhours_unknown_char, NULL);
1204 static gint ett_nt_logon_hours_hours = -1;
1207 dissect_LOGON_HOURS_hours(tvbuff_t *tvb, int offset,
1208 packet_info *pinfo, proto_tree *parent_tree,
1211 proto_item *item=NULL;
1212 proto_tree *tree=NULL;
1213 int old_offset=offset;
1216 item = proto_tree_add_text(parent_tree, tvb, offset, -1,
1218 tree = proto_item_add_subtree(item, ett_nt_logon_hours_hours);
1221 offset = dissect_ndr_ucvarray(tvb, offset, pinfo, tree, drep,
1222 dissect_LOGON_HOURS_entry);
1224 proto_item_set_len(item, offset-old_offset);
1230 static gint ett_nt_logon_hours = -1;
1231 static int hf_logonhours_divisions = -1;
1234 dissect_ndr_nt_LOGON_HOURS(tvbuff_t *tvb, int offset,
1235 packet_info *pinfo, proto_tree *parent_tree,
1238 proto_item *item=NULL;
1239 proto_tree *tree=NULL;
1240 int old_offset=offset;
1242 ALIGN_TO_4_BYTES; /* strcture starts with short, but is aligned for longs */
1245 item = proto_tree_add_text(parent_tree, tvb, offset, -1,
1247 tree = proto_item_add_subtree(item, ett_nt_logon_hours);
1250 offset = dissect_ndr_uint16(tvb, offset, pinfo, tree, drep,
1251 hf_logonhours_divisions, NULL);
1252 /* XXX - is this a bitmask like the "logon hours" field in the
1253 Remote API call "NetUserGetInfo()" with an information level
1255 offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
1256 dissect_LOGON_HOURS_hours, NDR_POINTER_UNIQUE,
1259 proto_item_set_len(item, offset-old_offset);
1264 dissect_ndr_nt_PSID_no_hf(tvbuff_t *tvb, int offset,
1265 packet_info *pinfo, proto_tree *parent_tree,
1268 offset=dissect_ndr_nt_PSID(tvb, offset, pinfo, parent_tree, drep);
1273 dissect_ndr_nt_PSID_ARRAY_sids (tvbuff_t *tvb, int offset,
1274 packet_info *pinfo, proto_tree *tree,
1277 offset = dissect_ndr_ucarray(tvb, offset, pinfo, tree, drep,
1278 dissect_ndr_nt_PSID_no_hf);
1283 static gint ett_nt_sid_array = -1;
1286 dissect_ndr_nt_PSID_ARRAY(tvbuff_t *tvb, int offset,
1287 packet_info *pinfo, proto_tree *parent_tree,
1291 proto_item *item=NULL;
1292 proto_tree *tree=NULL;
1293 int old_offset=offset;
1296 item = proto_tree_add_text(parent_tree, tvb, offset, -1,
1298 tree = proto_item_add_subtree(item, ett_nt_sid_array);
1301 offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
1302 hf_nt_count, &count);
1303 offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
1304 dissect_ndr_nt_PSID_ARRAY_sids, NDR_POINTER_UNIQUE,
1307 proto_item_set_len(item, offset-old_offset);
1311 static gint ett_nt_sid_and_attributes = -1;
1312 static int hf_nt_attrib = -1;
1315 dissect_ndr_nt_SID_AND_ATTRIBUTES(tvbuff_t *tvb, int offset,
1316 packet_info *pinfo, proto_tree *parent_tree,
1319 proto_item *item=NULL;
1320 proto_tree *tree=NULL;
1323 item = proto_tree_add_text(parent_tree, tvb, offset, 0,
1324 "SID_AND_ATTRIBUTES:");
1325 tree = proto_item_add_subtree(item, ett_nt_sid_and_attributes);
1328 offset = dissect_ndr_nt_PSID(tvb, offset, pinfo, tree, drep);
1330 offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
1331 hf_nt_attrib, NULL);
1336 static gint ett_nt_sid_and_attributes_array = -1;
1339 dissect_ndr_nt_SID_AND_ATTRIBUTES_ARRAY(tvbuff_t *tvb, int offset,
1340 packet_info *pinfo, proto_tree *parent_tree,
1343 proto_item *item=NULL;
1344 proto_tree *tree=NULL;
1345 int old_offset=offset;
1348 item = proto_tree_add_text(parent_tree, tvb, offset, 0,
1349 "SID_AND_ATTRIBUTES array:");
1350 tree = proto_item_add_subtree(item, ett_nt_sid_and_attributes_array);
1353 /*offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
1354 hf_samr_count, &count); */
1355 offset = dissect_ndr_ucarray(tvb, offset, pinfo, tree, drep,
1356 dissect_ndr_nt_SID_AND_ATTRIBUTES);
1358 proto_item_set_len(item, offset-old_offset);
1363 * Register ett/hf values and perform DCERPC over SMB specific
1366 void dcerpc_smb_init(int proto_dcerpc)
1368 static hf_register_info hf[] = {
1370 /* String handling */
1373 { "Size", "nt.str.size", FT_UINT16, BASE_DEC,
1374 NULL, 0x0, "Size of string in short integers",
1378 { "Length", "nt.str.len", FT_UINT16, BASE_DEC,
1379 NULL, 0x0, "Length of string in short integers",
1382 /* Policy handles */
1384 { &hf_nt_policy_open_frame,
1385 { "Frame handle opened", "dcerpc.nt.open_frame",
1386 FT_FRAMENUM, BASE_NONE, NULL, 0x0,
1387 "Frame handle opened", HFILL }},
1389 { &hf_nt_policy_close_frame,
1390 { "Frame handle closed", "dcerpc.nt.close_frame",
1391 FT_FRAMENUM, BASE_NONE, NULL, 0x0,
1392 "Frame handle closed", HFILL }},
1397 { "Acct Ctrl", "nt.acct_ctrl", FT_UINT32, BASE_HEX,
1398 NULL, 0x0, "Acct CTRL", HFILL }},
1400 { &hf_nt_acb_disabled,
1401 { "", "nt.acb.disabled", FT_BOOLEAN, 32,
1402 TFS(&tfs_nt_acb_disabled), 0x0001,
1403 "If this account is enabled or disabled", HFILL }},
1405 { &hf_nt_acb_homedirreq,
1406 { "", "nt.acb.homedirreq", FT_BOOLEAN, 32,
1407 TFS(&tfs_nt_acb_homedirreq), 0x0002,
1408 "Is hom,edirs required for this account?", HFILL }},
1410 { &hf_nt_acb_pwnotreq,
1411 { "", "nt.acb.pwnotreq", FT_BOOLEAN, 32,
1412 TFS(&tfs_nt_acb_pwnotreq), 0x0004,
1413 "If a password is required for this account?", HFILL }},
1415 { &hf_nt_acb_tempdup,
1416 { "", "nt.acb.tempdup", FT_BOOLEAN, 32,
1417 TFS(&tfs_nt_acb_tempdup), 0x0008,
1418 "If this is a temporary duplicate account", HFILL }},
1420 { &hf_nt_acb_normal,
1421 { "", "nt.acb.normal", FT_BOOLEAN, 32,
1422 TFS(&tfs_nt_acb_normal), 0x0010,
1423 "If this is a normal user account", HFILL }},
1426 { "", "nt.acb.mns", FT_BOOLEAN, 32,
1427 TFS(&tfs_nt_acb_mns), 0x0020,
1428 "MNS logon user account", HFILL }},
1430 { &hf_nt_acb_domtrust,
1431 { "", "nt.acb.domtrust", FT_BOOLEAN, 32,
1432 TFS(&tfs_nt_acb_domtrust), 0x0040,
1433 "Interdomain trust account", HFILL }},
1435 { &hf_nt_acb_wstrust,
1436 { "", "nt.acb.wstrust", FT_BOOLEAN, 32,
1437 TFS(&tfs_nt_acb_wstrust), 0x0080,
1438 "Workstation trust account", HFILL }},
1440 { &hf_nt_acb_svrtrust,
1441 { "", "nt.acb.svrtrust", FT_BOOLEAN, 32,
1442 TFS(&tfs_nt_acb_svrtrust), 0x0100,
1443 "Server trust account", HFILL }},
1445 { &hf_nt_acb_pwnoexp,
1446 { "", "nt.acb.pwnoexp", FT_BOOLEAN, 32,
1447 TFS(&tfs_nt_acb_pwnoexp), 0x0200,
1448 "If this account expires or not", HFILL }},
1450 { &hf_nt_acb_autolock,
1451 { "", "nt.acb.autolock", FT_BOOLEAN, 32,
1452 TFS(&tfs_nt_acb_autolock), 0x0400,
1453 "If this account has been autolocked", HFILL }},
1457 { &hf_nt_domain_sid,
1458 { "Domain SID", "nt.domain_sid",
1459 FT_STRING, BASE_NONE, NULL, 0x0,
1460 "The Domain SID", HFILL }},
1463 { "Count", "nt.count",
1464 FT_UINT32, BASE_DEC, NULL, 0x0,
1465 "Number of elements in following array", HFILL }},
1469 { &hf_logonhours_divisions,
1470 { "Divisions", "logonhours.divisions",
1471 FT_UINT16, BASE_DEC, NULL, 0,
1472 "Number of divisions for LOGON_HOURS", HFILL }},
1474 { &hf_logonhours_unknown_char,
1475 { "Unknown char", "nt.unknown.char",
1476 FT_UINT8, BASE_HEX, NULL, 0x0,
1477 "Unknown char. If you know what this is, contact "
1478 "ethereal developers.", HFILL }},
1483 { "Attributes", "nt.attr",
1484 FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL }},
1487 static gint *ett[] = {
1488 &ett_nt_unicode_string,
1489 &ett_nt_counted_string,
1490 &ett_nt_counted_byte_array,
1492 &ett_nt_sid_pointer,
1494 &ett_nt_logon_hours,
1495 &ett_nt_logon_hours_hours,
1497 &ett_nt_sid_and_attributes_array,
1498 &ett_nt_sid_and_attributes,
1501 /* Register ett's and hf's */
1503 proto_register_subtree_array(ett, array_length(ett));
1504 proto_register_field_array(proto_dcerpc, hf, array_length(hf));
1506 /* Initialise policy handle hash */