The function pointer in a "per_choice_t" or a "per_sequence_t" is to a
[obnox/wireshark/wip.git] / packet-dcerpc-nt.c
1 /* packet-dcerpc-nt.c
2  * Routines for DCERPC over SMB packet disassembly
3  * Copyright 2001-2003, Tim Potter <tpot@samba.org>
4  *
5  * $Id: packet-dcerpc-nt.c,v 1.80 2004/05/19 04:52:31 tpot Exp $
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@ethereal.com>
9  * Copyright 1998 Gerald Combs
10  *
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.
15  *
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.
20  *
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.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include <glib.h>
31 #include <epan/packet.h>
32 #include "packet-dcerpc.h"
33 #include "packet-dcerpc-nt.h"
34 #include "smb.h"
35 #include "packet-smb-common.h" /* for dissect_smb_64bit_time() */
36
37 /*
38  * This file contains helper routines that are used by the DCERPC over SMB
39  * dissectors for ethereal.
40  */
41
42 /*
43  * Used by several dissectors.
44  */
45 const value_string platform_id_vals[] = {
46         { 300, "DOS" },
47         { 400, "OS/2" },
48         { 500, "Windows NT" },
49         { 600, "OSF" },
50         { 700, "VMS" },
51         { 0,   NULL }
52 };
53
54 /* Parse some common RPC structures */
55
56 gint ett_nt_unicode_string = -1; /* FIXME: make static */
57
58 /* Dissect a counted string as a callback to dissect_ndr_pointer_cb() */
59
60 static int hf_nt_cs_len = -1;
61 static int hf_nt_cs_size = -1;
62
63 int
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,
68                               void *callback_args)
69 {
70         dcerpc_info *di = pinfo->private_data;
71         guint16 len, size;
72
73         /* Structure starts with short, but is aligned for longs */
74
75         ALIGN_TO_4_BYTES;
76
77         if (di->conformant_run)
78                 return offset;
79         
80         /* 
81            struct {
82                short len;
83                short size;
84                [size_is(size/2), length_is(len/2), ptr] unsigned short *string;
85            } UNICODE_STRING;
86
87          */
88
89         offset = dissect_ndr_uint16(tvb, offset, pinfo, tree, drep,
90                         hf_nt_cs_len, &len);
91
92         offset = dissect_ndr_uint16(tvb, offset, pinfo, tree, drep,
93                         hf_nt_cs_size, &size);  
94
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);
98
99         return offset;
100 }
101
102 static gint ett_nt_counted_string = -1;
103
104 static int
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)
109 {
110         proto_item *item;
111         proto_tree *subtree = tree;
112
113         if (add_subtree) {
114
115                 item = proto_tree_add_text(
116                         tree, tvb, offset, 0, 
117                         proto_registrar_get_name(hf_index));
118
119                 subtree = proto_item_add_subtree(item, ett_nt_counted_string);
120         }
121
122         /*
123          * Add 2 levels, so that the string gets attached to the
124          * "Character Array" top-level item and to the top-level item
125          * added above.
126          */
127         return dissect_ndr_counted_string_cb(
128                 tvb, offset, pinfo, subtree, drep, hf_index,
129                 cb_wstr_postprocess, GINT_TO_POINTER(2 + levels));
130 }
131
132 /* Dissect a counted string in-line. */
133
134 int
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)
138 {
139         return dissect_ndr_counted_string_helper(
140                 tvb, offset, pinfo, tree, drep, hf_index, levels, TRUE);
141 }
142
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. */
146
147 int
148 dissect_ndr_counted_string_ptr(tvbuff_t *tvb, int offset,
149                                packet_info *pinfo, proto_tree *tree,
150                                guint8 *drep)
151 {
152         dcerpc_info *di = pinfo->private_data;
153
154         return dissect_ndr_counted_string_helper(
155                 tvb, offset, pinfo, tree, drep, di->hf_index, 0, FALSE);
156 }
157
158 /* Dissect a counted byte_array as a callback to dissect_ndr_pointer_cb() */
159
160 static gint ett_nt_counted_byte_array = -1;
161
162 /* Dissect a counted byte array in-line. */
163
164 int
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,
169                                   void *callback_args)
170 {
171         dcerpc_info *di = pinfo->private_data;
172         proto_item *item;
173         proto_tree *subtree;
174         guint16 len, size;
175
176         /* Structure starts with short, but is aligned for longs */
177
178         ALIGN_TO_4_BYTES;
179
180         if (di->conformant_run)
181                 return offset;
182
183         item = proto_tree_add_text(tree, tvb, offset, 0, 
184                 proto_registrar_get_name(hf_index));
185
186         subtree = proto_item_add_subtree(item, ett_nt_counted_byte_array);
187         
188         /* 
189            struct {
190                short len;
191                short size;
192                [size_is(size), length_is(len), ptr] unsigned char *string;
193            } WHATEVER_THIS_IS_CALLED;
194
195          */
196
197         offset = dissect_ndr_uint16(tvb, offset, pinfo, subtree, drep,
198                         hf_nt_cs_len, &len);
199
200         offset = dissect_ndr_uint16(tvb, offset, pinfo, subtree, drep,
201                         hf_nt_cs_size, &size);  
202
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);
206
207         return offset;
208 }
209
210 int
211 dissect_ndr_counted_byte_array(tvbuff_t *tvb, int offset,
212                                packet_info *pinfo, proto_tree *tree,
213                                guint8 *drep, int hf_index)
214 {
215         return dissect_ndr_counted_byte_array_cb(
216                 tvb, offset, pinfo, tree, drep, hf_index, NULL, NULL);
217 }
218
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
222  */
223 int
224 dissect_ndr_nt_NTTIME (tvbuff_t *tvb, int offset,
225                         packet_info *pinfo, proto_tree *tree,
226                         guint8 *drep _U_, int hf_index)
227 {
228         dcerpc_info *di;
229
230         di=pinfo->private_data;
231         if(di->conformant_run){
232                 /*just a run to handle conformant arrays, nothing to dissect */
233                 return offset;
234         }
235
236         ALIGN_TO_4_BYTES;
237
238         offset = dissect_smb_64bit_time(tvb, tree, offset, hf_index);
239         return offset;
240 }
241
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. */
246
247 #undef DEBUG_HASH_COLL
248
249 /*
250  * Policy handle hashing.
251  *
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.
257  *
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
263  * those two pipes.
264  *
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".
269  *
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).
274  */
275
276 typedef struct {
277         guint8 policy_hnd[20];
278 } pol_hash_key;
279
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 */
286 } pol_value;
287
288 typedef struct {
289         pol_value *list;                 /* List of policy handle entries */
290 } pol_hash_value;
291
292 #define POL_HASH_INIT_COUNT 100
293
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;
298
299 /* Hash function */
300
301 static guint pol_hash_fn(gconstpointer k)
302 {
303         const pol_hash_key *key = (const pol_hash_key *)k;
304
305         /* Bytes 4-7 of the policy handle are a timestamp so should make a
306            reasonable hash value */
307
308         return key->policy_hnd[4] + (key->policy_hnd[5] << 8) +
309                 (key->policy_hnd[6] << 16) + (key->policy_hnd[7] << 24);
310 }
311
312 /* Return true if a policy handle is all zeros */
313
314 static gboolean is_null_pol(e_ctx_hnd *policy_hnd)
315 {
316         static guint8 null_policy_hnd[20];
317
318         return memcmp(policy_hnd, null_policy_hnd, 20) == 0;
319 }
320
321 /* Hash compare function */
322
323 static gint pol_hash_compare(gconstpointer k1, gconstpointer k2)
324 {
325         const pol_hash_key *key1 = (const pol_hash_key *)k1;
326         const pol_hash_key *key2 = (const pol_hash_key *)k2;
327
328         return memcmp(key1->policy_hnd, key2->policy_hnd,
329                       sizeof(key1->policy_hnd)) == 0;
330 }
331
332 /*
333  * Look up the instance of a policy handle value in whose range of frames
334  * the specified frame falls.
335  */
336 static pol_value *find_pol_handle(e_ctx_hnd *policy_hnd, guint32 frame,
337                                   pol_hash_value **valuep)
338 {
339         pol_hash_key key;
340         pol_value *pol;
341
342         memcpy(&key.policy_hnd, policy_hnd, sizeof(key.policy_hnd));
343         if ((*valuep = g_hash_table_lookup(pol_hash, &key))) {
344                 /*
345                  * Look for the first value such that both:
346                  *
347                  *      1) the first frame in which it was seen is
348                  *         <= the specified frame;
349                  *
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.
356                  *
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.
362                  */
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 */
368                 }
369                 return pol;
370         } else {
371                 /*
372                  * The handle isn't in the hash table.
373                  */
374                 return NULL;
375         }
376 }
377
378 static void add_pol_handle(e_ctx_hnd *policy_hnd, guint32 frame,
379                            pol_value *pol, pol_hash_value *value)
380 {
381         pol_hash_key *key;
382         pol_value *polprev, *polnext;
383
384         if (value == NULL) {
385                 /*
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
389                  * table.
390                  */
391                 value = g_mem_chunk_alloc(pol_hash_value_chunk);
392                 value->list = pol;
393                 pol->next = NULL;
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);
397         } else {
398                 /*
399                  * Put the new value in the hash value's policy handle
400                  * list so that it's sorted by the first frame in
401                  * which it appeared.
402                  *
403                  * Search for the first entry whose first frame number
404                  * is greater than the current frame number, if any.
405                  */
406                 for (polnext = value->list, polprev = NULL;
407                     polnext != NULL && polnext->first_frame <= frame;
408                     polprev = polnext, polnext = polnext->next)
409                         ;
410
411                 /*
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
415                  * the list.
416                  */
417                 if (polprev == NULL)
418                         value->list = pol;
419                 else
420                         polprev->next = pol;
421                 
422                 /*
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.
426                  */
427                 pol->next = polnext;
428         }
429 }
430
431 /* Store the open and close frame numbers of a policy handle */
432
433 void dcerpc_smb_store_pol_pkts(e_ctx_hnd *policy_hnd, packet_info *pinfo,
434                                gboolean is_open, gboolean is_close)
435 {
436         pol_hash_value *value;
437         pol_value *pol;
438
439         /*
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.
443          */
444         if (pinfo->fd->flags.visited)
445                 return;
446
447         if (is_null_pol(policy_hnd))
448                 return;
449
450         /* Look up existing value */
451         pol = find_pol_handle(policy_hnd, pinfo->fd->num, &value);
452
453         if (pol != NULL) {
454                 /*
455                  * Update the existing value as appropriate.
456                  */
457                 if (is_open) {
458                         /*
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
462                          * see it.
463                          *
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
468                          * just filled in.
469                          *
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.
474                          */
475                         if (pol->first_frame == pinfo->fd->num &&
476                             pol->last_frame == 0)
477                                 return;
478                         pol->last_frame = pinfo->fd->num;
479                         pol = NULL;
480                 } else {
481                         if (is_close) {
482                                 pol->close_frame = pinfo->fd->num;
483                                 pol->last_frame = pinfo->fd->num;
484                         }
485                         return;
486                 }
487         }
488
489         /* Create a new value */
490
491         pol = g_mem_chunk_alloc(pol_value_chunk);
492
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 */
497
498         pol->name = NULL;
499
500         add_pol_handle(policy_hnd, pinfo->fd->num, pol, value);
501 }
502
503 /* Store a text string with a policy handle */
504
505 void dcerpc_smb_store_pol_name(e_ctx_hnd *policy_hnd, packet_info *pinfo,
506                                char *name)
507 {
508         pol_hash_value *value;
509         pol_value *pol;
510
511         /*
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.
515          */
516         if (pinfo->fd->flags.visited)
517                 return;
518
519         if (is_null_pol(policy_hnd))
520                 return;
521
522         /* Look up existing value */
523         pol = find_pol_handle(policy_hnd, pinfo->fd->num, &value);
524
525         if (pol != NULL) {
526                 /*
527                  * This is the first pass; update the existing
528                  * value as appropriate.
529                  */
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);
534 #endif
535                         free(pol->name);
536                 }
537
538                 pol->name = strdup(name);
539
540                 return;
541         }
542
543         /* Create a new value */
544
545         pol = g_mem_chunk_alloc(pol_value_chunk);
546
547         pol->open_frame = 0;
548         pol->close_frame = 0;
549         pol->first_frame = pinfo->fd->num;
550         pol->last_frame = 0;
551
552         if (name)
553                 pol->name = strdup(name);
554         else
555                 pol->name = strdup("<UNKNOWN>");
556
557         add_pol_handle(policy_hnd, pinfo->fd->num, pol, value);
558 }
559
560 /*
561  * Retrieve a policy handle.
562  *
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
565  * close operations?
566  */
567
568 gboolean dcerpc_smb_fetch_pol(e_ctx_hnd *policy_hnd, char **name,
569                               guint32 *open_frame, guint32 *close_frame,
570                               guint32 cur_frame)
571 {
572         pol_hash_value *value;
573         pol_value *pol;
574
575         /* Prevent uninitialised return vars */
576
577         if (name)
578                 *name = NULL;
579
580         if (open_frame)
581                 *open_frame = 0;
582
583         if (close_frame)
584                 *close_frame = 0;
585
586         /* Look up existing value */
587         pol = find_pol_handle(policy_hnd, cur_frame, &value);
588
589         if (pol) {
590                 if (name)
591                         *name = pol->name;
592
593                 if (open_frame)
594                         *open_frame = pol->open_frame;
595
596                 if (close_frame)
597                         *close_frame = pol->close_frame;
598         }
599
600         return pol != NULL;
601 }
602
603 /* Iterator to free a policy handle key/value pair, and all
604    the policy handle values to which the hash table value
605    points */
606
607 static void free_pol_keyvalue(gpointer key _U_, gpointer value_arg,
608     gpointer user_data _U_)
609 {
610         pol_hash_value *value = (pol_hash_value *)value_arg;
611         pol_value *pol;
612
613         /* Free user data */
614
615         for (pol = value->list; pol != NULL; pol = pol->next) {
616                 free(pol->name);
617                 pol->name = NULL;
618         }
619 }
620
621 /* Initialise policy handle hash */
622
623 static void init_pol_hash(void)
624 {
625         /* Initialise memory chunks */
626
627         if (pol_hash_key_chunk)
628                 g_mem_chunk_destroy(pol_hash_key_chunk);
629
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);
633
634         if (pol_value_chunk)
635                 g_mem_chunk_destroy(pol_value_chunk);
636
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);
640
641         if (pol_hash_value_chunk)
642                 g_mem_chunk_destroy(pol_hash_value_chunk);
643
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);
647
648         /* Initialise hash table */
649
650         if (pol_hash) {
651                 g_hash_table_foreach(pol_hash, free_pol_keyvalue, NULL);
652                 g_hash_table_destroy(pol_hash);
653         }
654
655         pol_hash = g_hash_table_new(pol_hash_fn, pol_hash_compare);
656 }
657
658 /* Dissect a NT status code */
659
660 int
661 dissect_ntstatus(tvbuff_t *tvb, gint offset, packet_info *pinfo,
662                  proto_tree *tree, guint8 *drep,
663                  int hfindex, guint32 *pdata)
664 {
665         guint32 status;
666
667         offset = dissect_ndr_uint32(tvb, offset, pinfo, tree, drep,
668                                     hfindex, &status);
669
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"));
674         if (pdata)
675                 *pdata = status;
676
677         return offset;
678 }
679
680 /* Dissect a DOS status code */
681
682 int
683 dissect_doserror(tvbuff_t *tvb, gint offset, packet_info *pinfo,
684                proto_tree *tree, guint8 *drep,
685                int hfindex, guint32 *pdata)
686 {
687         guint32 status;
688
689         offset = dissect_ndr_uint32(tvb, offset, pinfo, tree, drep,
690                                     hfindex, &status);
691
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"));
696         if (pdata)
697                 *pdata = status;
698
699         return offset;
700 }
701
702 /* Dissect a NT policy handle */
703
704 static int hf_nt_policy_open_frame = -1;
705 static int hf_nt_policy_close_frame = -1;
706
707 static gint ett_nt_policy_hnd = -1;
708
709 int
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)
714 {
715         proto_item *item;
716         proto_tree *subtree;
717         e_ctx_hnd hnd;
718         guint32 open_frame = 0, close_frame = 0;
719         char *name;
720         int old_offset = offset;
721         dcerpc_info *di;
722
723         di=pinfo->private_data;
724         if(di->conformant_run){
725                 /*
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
729                  * case
730                  */
731                 return offset;
732         }
733
734         /* Add to proto tree */
735
736         item = proto_tree_add_text(tree, tvb, offset, sizeof(e_ctx_hnd),
737                                    "Policy Handle");
738
739         subtree = proto_item_add_subtree(item, ett_nt_policy_hnd);
740
741         offset = dissect_ndr_ctx_hnd(tvb, offset, pinfo, subtree, drep,
742                                      hfindex, &hnd);
743
744         /*
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.
748          */
749         dcerpc_smb_store_pol_pkts(&hnd, pinfo, is_open, is_close);
750
751         /* Insert open/close/name information if known */
752
753         if (dcerpc_smb_fetch_pol(&hnd, &name, &open_frame, &close_frame,
754             pinfo->fd->num)) {
755
756                 if (open_frame)
757                         proto_tree_add_uint(
758                                 subtree, hf_nt_policy_open_frame, tvb,
759                                 old_offset, sizeof(e_ctx_hnd), open_frame);
760
761                 if (close_frame)
762                         proto_tree_add_uint(
763                                 subtree, hf_nt_policy_close_frame, tvb,
764                                 old_offset, sizeof(e_ctx_hnd), close_frame);
765
766                 /*
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
770                  * so.
771                  */
772                 if (name != NULL && pitem == NULL)
773                         proto_item_append_text(item, ": %s", name);
774         }
775
776         if (pdata)
777                 *pdata = hnd;
778
779         if (pitem)
780                 *pitem = item;
781
782         return offset;
783 }
784
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. */
789
790 int
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)
794 {
795     const guint8 *data;
796
797     data = (const guint8 *)tvb_get_ptr(tvb, offset, length);
798
799     if (tree) {
800         proto_tree_add_item (tree, hfindex, tvb, offset, length, (drep[0] & 0x10));
801     }
802
803     if (pdata)
804         *pdata = data;
805
806     return offset + length;
807 }
808
809 int
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)
813 {
814     dcerpc_info *di;
815
816     di=pinfo->private_data;
817     if(di->conformant_run){
818       /* just a run to handle conformant arrays, no scalars to dissect */
819       return offset;
820     }
821
822     /* no alignment needed */
823     return dissect_dcerpc_uint8s(tvb, offset, pinfo,
824                                  tree, drep, hfindex, length, pdata);
825 }
826
827 int
828 dissect_dcerpc_uint16s(tvbuff_t *tvb, gint offset, packet_info *pinfo _U_,
829                       proto_tree *tree, guint8 *drep, int hfindex,
830                       int length)
831 {
832     if (tree) {
833         proto_tree_add_item (tree, hfindex, tvb, offset, length * 2, (drep[0] & 0x10));
834     }
835
836     return offset + length * 2;
837 }
838
839 int
840 dissect_ndr_uint16s(tvbuff_t *tvb, gint offset, packet_info *pinfo,
841                    proto_tree *tree, guint8 *drep,
842                    int hfindex, int length)
843 {
844     dcerpc_info *di;
845
846     di=pinfo->private_data;
847     if(di->conformant_run){
848       /* just a run to handle conformant arrays, no scalars to dissect */
849       return offset;
850     }
851
852     if (offset % 2)
853         offset++;
854
855     return dissect_dcerpc_uint16s(tvb, offset, pinfo,
856                                  tree, drep, hfindex, length);
857 }
858
859 /*
860  * Helper routines for dissecting NDR strings
861  */
862
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,
866                         void *callback_args)
867 {
868         gint options = GPOINTER_TO_INT(callback_args);
869         gint levels = CB_STR_ITEM_LEVELS(options);
870         char *s;
871
872         /* Align start_offset on 4-byte boundary. */
873
874         if (start_offset % 4)
875                 start_offset += 4 - (start_offset % 4);
876
877         /* Get string value */
878
879         if ((end_offset - start_offset) <= 12)
880                 return;         /* XXX: Use unistr2 dissector instead? */
881
882         /*
883          * XXX - need to handle non-printable characters here.
884          *
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?
889          */
890         s = tvb_fake_unicode(
891                 tvb, start_offset + 12, (end_offset - start_offset - 12) / 2,
892                 TRUE);
893
894         /* Append string to COL_INFO */
895
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);
899         }
900
901         /* Append string to upper-level proto_items */
902
903         if (levels > 0 && item && s && s[0]) {
904                 proto_item_append_text(item, ": %s", s);
905                 item = item->parent;
906                 levels--;
907                 if (levels > 0) {
908                         proto_item_append_text(item, ": %s", s);
909                         item = item->parent;
910                         levels--;
911                         while (levels > 0) {
912                                 proto_item_append_text(item, " %s", s);
913                                 item = item->parent;
914                                 levels--;
915                         }
916                 }
917         }
918
919         /* Save string to dcv->private_data */
920
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;
924                 
925                 dcv->private_data = g_strdup(s);
926         }
927
928         g_free(s);
929 }
930
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,
934                         void *callback_args)
935 {
936         gint options = GPOINTER_TO_INT(callback_args);
937         gint levels = CB_STR_ITEM_LEVELS(options);
938         char *s;
939
940         /* Align start_offset on 4-byte boundary. */
941
942         if (start_offset % 4)
943                 start_offset += 4 - (start_offset % 4);
944
945         /* Get string value */
946
947         if ((end_offset - start_offset) <= 12)
948                 return;         /* XXX: Use unistr2 dissector instead? */
949
950         /*
951          * XXX - need to handle non-printable characters here.
952          *
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?
957          */
958         s = tvb_get_string(
959                 tvb, start_offset + 12, (end_offset - start_offset - 12) );
960
961         /* Append string to COL_INFO */
962
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);
966         }
967
968         /* Append string to upper-level proto_items */
969
970         if (levels > 0 && item && s && s[0]) {
971                 proto_item_append_text(item, ": %s", s);
972                 item = item->parent;
973                 levels--;
974                 if (levels > 0) {
975                         proto_item_append_text(item, ": %s", s);
976                         item = item->parent;
977                         levels--;
978                         while (levels > 0) {
979                                 proto_item_append_text(item, " %s", s);
980                                 item = item->parent;
981                                 levels--;
982                         }
983                 }
984         }
985
986         /* Save string to dcv->private_data */
987
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;
991                 
992                 dcv->private_data = g_strdup(s);
993         }
994
995         g_free(s);
996 }
997
998 /* Dissect a pointer to a NDR string and append the string value to the
999    proto_item. */
1000
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)
1005 {
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));
1010 }
1011
1012 /* SID dissection routines */
1013
1014 static int hf_nt_count = -1;
1015 static int hf_nt_domain_sid = -1;
1016
1017 int
1018 dissect_ndr_nt_SID(tvbuff_t *tvb, int offset, packet_info *pinfo, 
1019                    proto_tree *tree, guint8 *drep)
1020 {
1021         dcerpc_info *di = (dcerpc_info *)pinfo->private_data;
1022         dcerpc_call_value *dcv = (dcerpc_call_value *)di->call_data;
1023         char *sid_str;
1024         char *name;
1025
1026         if(di->hf_index!=-1){
1027                 name=proto_registrar_get_name(di->hf_index);
1028         } else {
1029                 name="Domain";
1030         }
1031         if(di->conformant_run){
1032                 /* just a run to handle conformant arrays, no scalars to dissect */
1033                 return offset;
1034         }
1035
1036         /* the SID contains a conformant array, first we must eat
1037            the 4-byte max_count before we can hand it off */
1038
1039         offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
1040                         hf_nt_count, NULL);
1041
1042         offset = dissect_nt_sid(tvb, offset, tree, name, &sid_str, 
1043                                 hf_nt_domain_sid);
1044
1045         /* dcv can be null, for example when this ndr structure is embedded
1046          * inside non-dcerpc pdus, i.e. kerberos PAC structure
1047          */
1048         if(dcv){
1049                 dcv->private_data = sid_str;
1050         }
1051
1052         return offset;
1053 }
1054
1055 static int
1056 dissect_ndr_nt_SID_hf_through_ptr(tvbuff_t *tvb, int offset, packet_info *pinfo, 
1057                    proto_tree *tree, guint8 *drep)
1058 {
1059         offset = dissect_ndr_nt_SID(tvb, offset, pinfo, tree, drep);
1060
1061         return offset;
1062 }
1063
1064 static gint ett_nt_sid_pointer = -1;
1065
1066 int
1067 dissect_ndr_nt_PSID(tvbuff_t *tvb, int offset,
1068                     packet_info *pinfo, proto_tree *parent_tree,
1069                     guint8 *drep)
1070 {
1071         proto_item *item=NULL;
1072         proto_tree *tree=NULL;
1073         int old_offset=offset;
1074
1075         if(parent_tree){
1076                 item = proto_tree_add_text(parent_tree, tvb, offset, -1,
1077                         "SID pointer:");
1078                 tree = proto_item_add_subtree(item, ett_nt_sid_pointer);
1079         }
1080
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);
1084
1085         proto_item_set_len(item, offset-old_offset);
1086         return offset;
1087 }
1088
1089 static const true_false_string tfs_nt_acb_disabled = {
1090         "Account is DISABLED",
1091         "Account is NOT disabled"
1092 };
1093 static const true_false_string tfs_nt_acb_homedirreq = {
1094         "Homedir is REQUIRED",
1095         "Homedir is NOT required"
1096 };
1097 static const true_false_string tfs_nt_acb_pwnotreq = {
1098         "Password is NOT required",
1099         "Password is REQUIRED"
1100 };
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"
1104 };
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"
1108 };
1109 static const true_false_string tfs_nt_acb_mns = {
1110         "This is a MNS account",
1111         "This is NOT a mns account"
1112 };
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"
1116 };
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"
1120 };
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"
1124 };
1125 static const true_false_string tfs_nt_acb_pwnoexp = {
1126         "Passwords does NOT expire",
1127         "Password will EXPIRE"
1128 };
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"
1132 };
1133
1134 static gint ett_nt_acct_ctrl = -1;
1135
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;
1148
1149 int
1150 dissect_ndr_nt_acct_ctrl(tvbuff_t *tvb, int offset, packet_info *pinfo,
1151                         proto_tree *parent_tree, guint8 *drep)
1152 {
1153         guint32 mask;
1154         proto_item *item = NULL;
1155         proto_tree *tree = NULL;
1156
1157         offset=dissect_ndr_uint32(tvb, offset, pinfo, NULL, drep,
1158                         hf_nt_acct_ctrl, &mask);
1159
1160         if(parent_tree){
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);
1164         }
1165
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);
1188
1189         return offset;
1190 }
1191
1192 static int hf_logonhours_unknown_char;
1193
1194 static int
1195 dissect_LOGON_HOURS_entry(tvbuff_t *tvb, int offset,
1196                           packet_info *pinfo, proto_tree *tree,
1197                           guint8 *drep)
1198 {
1199         offset = dissect_ndr_uint8(tvb, offset, pinfo, tree, drep,
1200                         hf_logonhours_unknown_char, NULL);
1201         return offset;
1202 }
1203
1204 static gint ett_nt_logon_hours_hours = -1;
1205
1206 static int
1207 dissect_LOGON_HOURS_hours(tvbuff_t *tvb, int offset,
1208                           packet_info *pinfo, proto_tree *parent_tree,
1209                           guint8 *drep)
1210 {
1211         proto_item *item=NULL;
1212         proto_tree *tree=NULL;
1213         int old_offset=offset;
1214
1215         if(parent_tree){
1216                 item = proto_tree_add_text(parent_tree, tvb, offset, -1,
1217                         "LOGON_HOURS:");
1218                 tree = proto_item_add_subtree(item, ett_nt_logon_hours_hours);
1219         }
1220
1221         offset = dissect_ndr_ucvarray(tvb, offset, pinfo, tree, drep,
1222                         dissect_LOGON_HOURS_entry);
1223
1224         proto_item_set_len(item, offset-old_offset);
1225         return offset;
1226
1227         return offset;
1228 }
1229
1230 static gint ett_nt_logon_hours = -1;
1231 static int hf_logonhours_divisions = -1;
1232
1233 int
1234 dissect_ndr_nt_LOGON_HOURS(tvbuff_t *tvb, int offset,
1235                         packet_info *pinfo, proto_tree *parent_tree,
1236                         guint8 *drep)
1237 {
1238         proto_item *item=NULL;
1239         proto_tree *tree=NULL;
1240         int old_offset=offset;
1241
1242         ALIGN_TO_4_BYTES;  /* strcture starts with short, but is aligned for longs */
1243
1244         if(parent_tree){
1245                 item = proto_tree_add_text(parent_tree, tvb, offset, -1,
1246                         "LOGON_HOURS:");
1247                 tree = proto_item_add_subtree(item, ett_nt_logon_hours);
1248         }
1249
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
1254            of 11? */
1255         offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
1256                         dissect_LOGON_HOURS_hours, NDR_POINTER_UNIQUE,
1257                         "LOGON_HOURS", -1);
1258
1259         proto_item_set_len(item, offset-old_offset);
1260         return offset;
1261 }
1262
1263 static int
1264 dissect_ndr_nt_PSID_no_hf(tvbuff_t *tvb, int offset,
1265                              packet_info *pinfo, proto_tree *parent_tree,
1266                              guint8 *drep)
1267 {
1268         offset=dissect_ndr_nt_PSID(tvb, offset, pinfo, parent_tree, drep);
1269         return offset;
1270 }
1271
1272 static int
1273 dissect_ndr_nt_PSID_ARRAY_sids (tvbuff_t *tvb, int offset,
1274                              packet_info *pinfo, proto_tree *tree,
1275                              guint8 *drep)
1276 {
1277         offset = dissect_ndr_ucarray(tvb, offset, pinfo, tree, drep,
1278                         dissect_ndr_nt_PSID_no_hf);
1279
1280         return offset;
1281 }
1282
1283 static gint ett_nt_sid_array = -1;
1284
1285 int
1286 dissect_ndr_nt_PSID_ARRAY(tvbuff_t *tvb, int offset,
1287                         packet_info *pinfo, proto_tree *parent_tree,
1288                         guint8 *drep)
1289 {
1290         guint32 count;
1291         proto_item *item=NULL;
1292         proto_tree *tree=NULL;
1293         int old_offset=offset;
1294
1295         if(parent_tree){
1296                 item = proto_tree_add_text(parent_tree, tvb, offset, -1,
1297                         "SID array:");
1298                 tree = proto_item_add_subtree(item, ett_nt_sid_array);
1299         }
1300
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,
1305                         "PSID_ARRAY", -1);
1306
1307         proto_item_set_len(item, offset-old_offset);
1308         return offset;
1309 }
1310
1311 static gint ett_nt_sid_and_attributes = -1;
1312 static int hf_nt_attrib = -1;
1313
1314 int
1315 dissect_ndr_nt_SID_AND_ATTRIBUTES(tvbuff_t *tvb, int offset,
1316                         packet_info *pinfo, proto_tree *parent_tree,
1317                         guint8 *drep)
1318 {
1319         proto_item *item=NULL;
1320         proto_tree *tree=NULL;
1321
1322         if(parent_tree){
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);
1326         }
1327
1328         offset = dissect_ndr_nt_PSID(tvb, offset, pinfo, tree, drep);
1329
1330         offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
1331                                      hf_nt_attrib, NULL);
1332
1333         return offset;
1334 }
1335
1336 static gint ett_nt_sid_and_attributes_array = -1;
1337
1338 int
1339 dissect_ndr_nt_SID_AND_ATTRIBUTES_ARRAY(tvbuff_t *tvb, int offset,
1340                         packet_info *pinfo, proto_tree *parent_tree,
1341                         guint8 *drep)
1342 {
1343         proto_item *item=NULL;
1344         proto_tree *tree=NULL;
1345         int old_offset=offset;
1346
1347         if(parent_tree){
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);
1351         }
1352
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);
1357
1358         proto_item_set_len(item, offset-old_offset);
1359         return offset;
1360 }
1361
1362 /*
1363  * Register ett/hf values and perform DCERPC over SMB specific
1364  * initialisation.
1365  */
1366 void dcerpc_smb_init(int proto_dcerpc)
1367 {
1368         static hf_register_info hf[] = {
1369
1370                 /* String handling */
1371
1372                 { &hf_nt_cs_size,
1373                   { "Size", "nt.str.size", FT_UINT16, BASE_DEC,
1374                     NULL, 0x0, "Size of string in short integers", 
1375                     HFILL }},
1376                 
1377                 { &hf_nt_cs_len,
1378                   { "Length", "nt.str.len", FT_UINT16, BASE_DEC,
1379                     NULL, 0x0, "Length of string in short integers", 
1380                     HFILL }},
1381                 
1382                 /* Policy handles */
1383
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 }},
1388
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 }},
1393
1394                 /* ACBs */
1395
1396                 { &hf_nt_acct_ctrl,
1397                   { "Acct Ctrl", "nt.acct_ctrl", FT_UINT32, BASE_HEX,
1398                     NULL, 0x0, "Acct CTRL", HFILL }},
1399
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 }},
1404
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 }},
1409
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 }},
1414
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 }},
1419
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 }},
1424
1425                 { &hf_nt_acb_mns, 
1426                   { "", "nt.acb.mns", FT_BOOLEAN, 32,
1427                     TFS(&tfs_nt_acb_mns), 0x0020, 
1428                     "MNS logon user account", HFILL }},
1429
1430                 { &hf_nt_acb_domtrust, 
1431                   { "", "nt.acb.domtrust", FT_BOOLEAN, 32,
1432                     TFS(&tfs_nt_acb_domtrust), 0x0040, 
1433                     "Interdomain trust account", HFILL }},
1434                 
1435                 { &hf_nt_acb_wstrust, 
1436                   { "", "nt.acb.wstrust", FT_BOOLEAN, 32,
1437                     TFS(&tfs_nt_acb_wstrust), 0x0080, 
1438                     "Workstation trust account", HFILL }},
1439                 
1440                 { &hf_nt_acb_svrtrust, 
1441                   { "", "nt.acb.svrtrust", FT_BOOLEAN, 32,
1442                     TFS(&tfs_nt_acb_svrtrust), 0x0100, 
1443                     "Server trust account", HFILL }},
1444
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 }},
1449
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 }},
1454
1455                 /* SIDs */
1456
1457                 { &hf_nt_domain_sid,
1458                   { "Domain SID", "nt.domain_sid", 
1459                     FT_STRING, BASE_NONE, NULL, 0x0, 
1460                     "The Domain SID", HFILL }},
1461
1462                 { &hf_nt_count,
1463                   { "Count", "nt.count", 
1464                     FT_UINT32, BASE_DEC, NULL, 0x0, 
1465                     "Number of elements in following array", HFILL }},
1466
1467                 /* Logon hours */
1468
1469                 { &hf_logonhours_divisions, 
1470                   { "Divisions", "logonhours.divisions", 
1471                     FT_UINT16, BASE_DEC, NULL, 0, 
1472                     "Number of divisions for LOGON_HOURS", HFILL }},
1473
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 }},
1479
1480                 /* Misc */
1481
1482                 { &hf_nt_attrib, 
1483                   { "Attributes", "nt.attr", 
1484                     FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL }},
1485         };
1486
1487         static gint *ett[] = {
1488                 &ett_nt_unicode_string,
1489                 &ett_nt_counted_string,
1490                 &ett_nt_counted_byte_array,
1491                 &ett_nt_policy_hnd,
1492                 &ett_nt_sid_pointer,
1493                 &ett_nt_acct_ctrl,
1494                 &ett_nt_logon_hours,
1495                 &ett_nt_logon_hours_hours,
1496                 &ett_nt_sid_array,
1497                 &ett_nt_sid_and_attributes_array,
1498                 &ett_nt_sid_and_attributes,
1499         };
1500
1501         /* Register ett's and hf's */
1502
1503         proto_register_subtree_array(ett, array_length(ett));
1504         proto_register_field_array(proto_dcerpc, hf, array_length(hf));
1505
1506         /* Initialise policy handle hash */
1507
1508         init_pol_hash();
1509 }