The length argument to "fake_unicode()" is in characters, not bytes.
[obnox/wireshark/wip.git] / packet-dcerpc-nt.c
1 /* packet-dcerpc-nt.c
2  * Routines for DCERPC over SMB packet disassembly
3  * Copyright 2001, Tim Potter <tpot@samba.org>
4  *
5  * $Id: packet-dcerpc-nt.c,v 1.20 2002/03/20 07:39:18 guy 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 /* Align offset to a n-byte boundary */
43
44 int prs_align(int offset, int n)
45 {
46         if (offset % n)
47                 offset += n - (offset % n);
48         
49         return offset;
50 }
51
52 /* Parse a 8-bit integer */
53
54 int prs_uint8(tvbuff_t *tvb, int offset, packet_info *pinfo,
55               proto_tree *tree, guint8 *data, char *name)
56 {
57         guint8 i;
58         
59         /* No alignment required */
60
61         i = tvb_get_guint8(tvb, offset);
62         offset++;
63
64         if (name && tree)
65                 proto_tree_add_text(tree, tvb, offset - 1, 1, 
66                                     "%s: %u", name, i);
67
68         if (data)
69                 *data = i;
70
71         return offset;
72 }
73
74 int prs_uint8s(tvbuff_t *tvb, int offset, packet_info *pinfo,
75                proto_tree *tree, int count, int *data_offset, char *name)
76 {
77         /* No alignment required */
78
79         if (name && tree)
80                 proto_tree_add_text(tree, tvb, offset, count, "%s", name);
81
82         if (data_offset)
83                 *data_offset = offset;
84
85         offset += count;
86
87         return offset;
88 }
89
90 /* Parse a 16-bit integer */
91
92 int prs_uint16(tvbuff_t *tvb, int offset, packet_info *pinfo,
93                proto_tree *tree, guint16 *data, char *name)
94 {
95         guint16 i;
96         
97         offset = prs_align(offset, 2);
98         
99         i = tvb_get_letohs(tvb, offset);
100         offset += 2;
101
102         if (name && tree)
103                 proto_tree_add_text(tree, tvb, offset - 2, 2, 
104                                     "%s: %u", name, i);
105         if (data)
106                 *data = i;
107
108         return offset;
109 }
110
111 /* Parse a number of uint16's */
112
113 int prs_uint16s(tvbuff_t *tvb, int offset, packet_info *pinfo,
114                 proto_tree *tree, int count, int *data_offset, char *name)
115 {
116         offset = prs_align(offset, 2);
117         
118         if (name && tree)
119                 proto_tree_add_text(tree, tvb, offset, count * 2, 
120                                     "%s", name);
121         if (data_offset)
122                 *data_offset = offset;
123
124         offset += count * 2;
125
126         return offset;
127 }
128
129 /* Parse a 32-bit integer */
130
131 int prs_uint32(tvbuff_t *tvb, int offset, packet_info *pinfo,
132                proto_tree *tree, guint32 *data, char *name)
133 {
134         guint32 i;
135         
136         offset = prs_align(offset, 4);
137         
138         i = tvb_get_letohl(tvb, offset);
139         offset += 4;
140
141         if (name && tree)
142                 proto_tree_add_text(tree, tvb, offset - 4, 4, 
143                                     "%s: %u", name, i);
144
145         if (data)
146                 *data = i;
147
148         return offset;
149 }
150
151 /* Parse a number of 32-bit integers */
152
153 int prs_uint32s(tvbuff_t *tvb, int offset, packet_info *pinfo,
154                 proto_tree *tree, int count, int *data_offset, char *name)
155 {
156         offset = prs_align(offset, 4);
157         
158         if (name && tree)
159                 proto_tree_add_text(tree, tvb, offset - 4, 4, 
160                                     "%s", name);
161         if (data_offset)
162                 *data_offset = offset;
163
164         offset += count * 4;
165
166         return offset;
167 }
168
169 /* Parse a NT status code */
170
171 int prs_ntstatus(tvbuff_t *tvb, int offset, packet_info *pinfo,
172                  proto_tree *tree)
173 {
174         guint32 status;
175
176         offset = prs_uint32(tvb, offset, pinfo, tree, &status, NULL);
177
178         if (tree)
179                 proto_tree_add_text(tree, tvb, offset - 4, 4, "Status: %s",
180                                     val_to_str(status, NT_errors, "???"));
181
182         return offset;
183 }
184
185 /*
186  * We need to keep track of deferred referrents as they appear in the
187  * packet after all the non-pointer objects.
188  * to keep track of pointers as they are parsed as scalars and need to be
189  * remembered for the next call to the prs function.
190  *
191  * Pointers are stored in a linked list and pushed in the PARSE_SCALARS
192  * section of the prs function and popped in the PARSE_BUFFERS section.  If
193  * we try to pop off a referrent that has a different name then we are
194  * expecting then something has gone wrong.
195  */
196
197 #undef DEBUG_PTRS
198
199 struct ptr {
200         char *name;
201         guint32 value;
202 };
203
204 /* Create a new pointer */
205
206 static struct ptr *new_ptr(char *name, guint32 value)
207 {
208         struct ptr *p;
209
210         p = g_malloc(sizeof(struct ptr));
211
212         p->name = g_strdup(name);
213         p->value = value;
214
215         return p;
216 }
217
218 /* Free a pointer */
219
220 static void free_ptr(struct ptr *p)
221 {
222         if (p) {
223                 g_free(p->name);
224                 g_free(p);
225         }
226 }
227
228 /* Parse a pointer and store it's value in a linked list */
229
230 int prs_push_ptr(tvbuff_t *tvb, int offset, packet_info *pinfo,
231                  proto_tree *tree, GList **ptr_list, char *name)
232 {
233         struct ptr *p;
234         guint32 value;
235
236         offset = prs_uint32(tvb, offset, pinfo, tree, &value, NULL);
237
238         if (name && tree)
239                 proto_tree_add_text(tree, tvb, offset - 4, 4, 
240                                     "%s pointer: 0x%08x", name, value);
241
242         p = new_ptr(name, value);
243
244         *ptr_list = g_list_append(*ptr_list, p);
245
246 #ifdef DEBUG_PTRS
247         fprintf(stderr, "DEBUG_PTRS: pushing %s ptr = 0x%08x, %d ptrs in "
248                 "list\n", name, value, g_list_length(*ptr_list));
249 #endif
250
251         return offset;
252 }
253
254 /* Pop a pointer of a given name.  Return it's value. */
255
256 guint32 prs_pop_ptr(GList **ptr_list, char *name)
257 {
258         GList *elt;
259         struct ptr *p;
260         guint32 result;
261
262         g_assert(g_list_length(*ptr_list) != 0);        /* List too short */
263
264         /* Get pointer at head of list */
265
266         elt = g_list_first(*ptr_list);
267         p = (struct ptr *)elt->data;
268         result = p->value;
269
270 #ifdef DEBUG_PTRS
271         if (strcmp(p->name, name) != 0) {
272                 fprintf(stderr, "DEBUG_PTRS: wrong pointer (%s != %s)\n",
273                         p->name, name);
274         }
275 #endif
276
277         /* Free pointer record */
278
279         *ptr_list = g_list_remove_link(*ptr_list, elt);
280
281 #ifdef DEBUG_PTRS
282         fprintf(stderr, "DEBUG_PTRS: popping %s ptr = 0x%08x, %d ptrs in "
283                 "list\n", p->name, p->value, g_list_length(*ptr_list));
284 #endif
285
286         free_ptr(p);
287
288         return result;
289 }
290
291 /*
292  * Parse a UNISTR2 structure 
293  *
294  * typedef struct {
295  *   short length;
296  *   short size;
297  *   [size_is(size/2)] [length_is(length/2)] [unique] wchar_t *string;
298  * } UNICODE_STRING;
299  *
300  */
301
302 /* Convert a string from little-endian unicode to ascii.  At the moment we
303    fake it by taking every odd byte.  )-:  The caller must free the
304    result returned. */
305
306 char *fake_unicode(tvbuff_t *tvb, int offset, int len)
307 {
308         char *buffer;
309         int i;
310         guint16 character;
311
312         /* Make sure we have enough data before allocating the buffer,
313            so we don't blow up if the length is huge.
314            We do so by attempting to fetch the last character; it'll
315            throw an exception if it's past the end. */
316         tvb_get_letohs(tvb, offset + 2*(len - 1));
317
318         /* We know we won't throw an exception, so we don't have to worry
319            about leaking this buffer. */
320         buffer = g_malloc(len + 1);
321
322         for (i = 0; i < len; i++) {
323                 character = tvb_get_letohs(tvb, offset);
324                 buffer[i] = character & 0xff;
325                 offset += 2;
326         }
327
328         buffer[len] = 0;
329
330         return buffer;
331 }
332
333 /* Parse a UNISTR2 structure */
334
335 int prs_UNISTR2(tvbuff_t *tvb, int offset, packet_info *pinfo,
336                 proto_tree *tree, int flags, char **data, char *name)
337 {
338         guint32 len = 0, unknown = 0, max_len = 0;
339
340         if (flags & PARSE_SCALARS) {
341                 offset = prs_uint32(tvb, offset, pinfo, tree, &len, "Length");
342                 offset = prs_uint32(tvb, offset, pinfo, tree, &unknown, 
343                                     "Offset");
344                 offset = prs_uint32(tvb, offset, pinfo, tree, &max_len, 
345                                     "Max length");
346         }
347
348         if (flags & PARSE_BUFFERS) {
349                 int data16_offset;
350
351                 offset = prs_uint16s(tvb, offset, pinfo, tree, max_len,
352                                      &data16_offset, "Buffer");
353
354                 if (data)
355                         *data = fake_unicode(tvb, data16_offset, max_len);
356         }
357
358         return offset;
359 }
360
361 /* Parse a policy handle. */
362
363 int prs_policy_hnd(tvbuff_t *tvb, int offset, packet_info *pinfo, 
364                    proto_tree *tree, const guint8 **data)
365 {
366         const guint8 *data8;
367
368         offset = prs_align(offset, 4);
369
370         proto_tree_add_text(tree, tvb, offset, 20, "Policy Handle: %s", 
371                 tvb_bytes_to_str(tvb, offset, 20));
372
373         data8 = tvb_get_ptr(tvb, offset, 20);
374         
375         if (data)
376                 *data = data8;
377
378         return offset + 20;
379 }
380
381
382
383 /* following are a few functions for dissecting common structures used by NT 
384    services. These might need to be cleaned up at a later time but at least we get
385    them out of the real service dissectors.
386 */
387
388
389 /* UNICODE_STRING  BEGIN */
390 /* functions to dissect a UNICODE_STRING structure, common to many 
391    NT services
392    struct {
393      short len;
394      short size;
395      [size_is(size/2), length_is(len/2), ptr] unsigned short *string;
396    } UNICODE_STRING;
397
398    these variables can be found in packet-dcerpc-samr.c 
399 */
400 extern int hf_nt_str_len;
401 extern int hf_nt_str_off;
402 extern int hf_nt_str_max_len;
403 extern int hf_nt_string_length;
404 extern int hf_nt_string_size;
405 extern gint ett_nt_unicode_string;
406
407
408 /* this function will dissect the
409      [size_is(size/2), length_is(len/2), ptr] unsigned short *string;
410   part of the unicode string
411
412    struct {
413      short len;
414      short size;
415      [size_is(size/2), length_is(len/2), ptr] unsigned short *string;
416    } UNICODE_STRING;
417   structure used by NT to transmit unicode string values.
418
419   This function also looks at di->levels to see if whoever called us wanted us to append
420   the name: string to any higher levels in the tree .
421 */
422 int
423 dissect_ndr_nt_UNICODE_STRING_str(tvbuff_t *tvb, int offset, 
424                         packet_info *pinfo, proto_tree *tree, 
425                         char *drep)
426 {
427         guint32 len, off, max_len;
428         int data16_offset;
429         char *text;
430         int old_offset;
431         dcerpc_info *di;
432
433         di=pinfo->private_data;
434         if(di->conformant_run){
435                 /*just a run to handle conformant arrays, nothing to dissect */
436                 return offset;
437         }
438
439         offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
440                         hf_nt_str_len, &len);
441         offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
442                         hf_nt_str_off, &off);
443         offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
444                         hf_nt_str_max_len, &max_len);
445
446         old_offset=offset;
447         offset = prs_uint16s(tvb, offset, pinfo, tree, max_len, &data16_offset,
448                         NULL);
449         text = fake_unicode(tvb, data16_offset, max_len);
450
451         proto_tree_add_string(tree, di->hf_index, tvb, old_offset,
452                 offset-old_offset, text);
453
454         /* need to test di->levels before doing the proto_item_append_text()
455            since netlogon has these objects as top level objects in its representation
456            and trying to append to the tree object in that case will dump core */
457         if(tree && (di->levels>-1)){
458                 proto_item_append_text(tree, ": %s", text);
459                 if(di->levels>-1){
460                         tree=tree->parent;
461                         proto_item_append_text(tree, ": %s", text);
462                         while(di->levels>0){
463                                 tree=tree->parent;
464                                 proto_item_append_text(tree, " %s", text);
465                                 di->levels--;
466                         }
467                 }
468         }
469         g_free(text);
470         return offset;
471 }
472
473 /* this function will dissect the
474    struct {
475      short len;
476      short size;
477      [size_is(size/2), length_is(len/2), ptr] unsigned short *string;
478    } UNICODE_STRING;
479   structure used by NT to transmit unicode string values.
480  
481   the function takes one additional parameter, level
482   which specifies how many additional levels up in the tree where we should
483   append the string.  If unsure, specify levels as 0.
484 */
485 int
486 dissect_ndr_nt_UNICODE_STRING(tvbuff_t *tvb, int offset, 
487                         packet_info *pinfo, proto_tree *parent_tree, 
488                         char *drep, int hf_index, int levels)
489 {
490         proto_item *item=NULL;
491         proto_tree *tree=NULL;
492         int old_offset=offset;
493         dcerpc_info *di;
494         char *name;
495
496         ALIGN_TO_4_BYTES;  /* strcture starts with short, but is aligned for longs */
497
498         di=pinfo->private_data;
499         if(di->conformant_run){
500                 /*just a run to handle conformant arrays, nothing to dissect */
501                 return offset;
502         }
503
504         name = proto_registrar_get_name(hf_index);
505         if(parent_tree){
506                 item = proto_tree_add_text(parent_tree, tvb, offset, -1,
507                         "%s", name);
508                 tree = proto_item_add_subtree(item, ett_nt_unicode_string);
509         }
510
511         offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
512                         hf_nt_string_length, NULL);
513         offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
514                         hf_nt_string_size, NULL);
515         di->levels=1;
516         offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
517                         dissect_ndr_nt_UNICODE_STRING_str, NDR_POINTER_UNIQUE,
518                         name, hf_index, levels);
519
520         proto_item_set_len(item, offset-old_offset);
521         return offset;
522 }
523 /* UNICODE_STRING  END */
524
525 /* functions to dissect a STRING structure, common to many 
526    NT services
527    struct {
528      short len;
529      short size;
530      [size_is(size), length_is(len), ptr] char *string;
531    } STRING;
532 */
533 int
534 dissect_ndr_nt_STRING_string (tvbuff_t *tvb, int offset, 
535                              packet_info *pinfo, proto_tree *tree, 
536                              char *drep)
537 {
538         guint32 len, off, max_len;
539         int text_offset;
540         const guint8 *text;
541         int old_offset;
542         header_field_info *hfi;
543         dcerpc_info *di;
544
545         di=pinfo->private_data;
546         if(di->conformant_run){
547                 /*just a run to handle conformant arrays, nothing to dissect */
548                 return offset;
549         }
550
551         offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
552                                      hf_nt_str_len, &len);
553         offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
554                                      hf_nt_str_off, &off);
555         offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
556                                      hf_nt_str_max_len, &max_len);
557
558         old_offset=offset;
559         hfi = proto_registrar_get_nth(di->hf_index);
560
561         switch(hfi->type){
562         case FT_STRING:
563                 offset = prs_uint8s(tvb, offset, pinfo, tree, max_len,
564                         &text_offset, NULL);
565                 text = tvb_get_ptr(tvb, text_offset, max_len);
566                 proto_tree_add_string_format(tree, di->hf_index, 
567                         tvb, old_offset, offset-old_offset,
568                         text, "%s: %s", hfi->name, text);
569                 break;
570         case FT_BYTES:
571                 text = NULL;
572                 proto_tree_add_item(tree, di->hf_index, tvb, offset, max_len, FALSE);
573                 offset += max_len;
574                 break;
575         default:
576                 text = NULL;
577                 g_assert_not_reached();
578         }
579
580         if(tree && text && (di->levels>-1)){
581                 proto_item_append_text(tree, ": %s", text);
582                 if(di->levels>-1){
583                         tree=tree->parent;
584                         proto_item_append_text(tree, ": %s", text);
585                         while(di->levels>0){
586                                 tree=tree->parent;
587                                 proto_item_append_text(tree, " %s", text);
588                                 di->levels--;
589                         }
590                 }
591         }
592         return offset;
593 }
594
595 int
596 dissect_ndr_nt_STRING (tvbuff_t *tvb, int offset, 
597                              packet_info *pinfo, proto_tree *parent_tree, 
598                              char *drep, int hf_index, int levels)
599 {
600         proto_item *item=NULL;
601         proto_tree *tree=NULL;
602         int old_offset=offset;
603         dcerpc_info *di;
604         char *name;
605
606         ALIGN_TO_4_BYTES;  /* strcture starts with short, but is aligned for longs */
607
608         di=pinfo->private_data;
609         if(di->conformant_run){
610                 /*just a run to handle conformant arrays, nothing to dissect */
611                 return offset;
612         }
613
614         name = proto_registrar_get_name(hf_index);
615         if(parent_tree){
616                 item = proto_tree_add_text(parent_tree, tvb, offset, -1,
617                         "%s", name);
618                 tree = proto_item_add_subtree(item, ett_nt_unicode_string);
619         }
620
621         offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
622                                      hf_nt_string_length, NULL);
623         offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
624                                      hf_nt_string_size, NULL);
625         offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
626                         dissect_ndr_nt_STRING_string, NDR_POINTER_UNIQUE,
627                         name, hf_index, levels);
628
629         proto_item_set_len(item, offset-old_offset);
630         return offset;
631 }
632
633
634 /* This function is used to dissect a DCERPC encoded 64 bit time value.
635    XXX it should be fixed both here and in dissect_smb_64bit_time so
636    it can handle both BIG and LITTLE endian encodings 
637  */
638 int
639 dissect_ndr_nt_NTTIME (tvbuff_t *tvb, int offset, 
640                         packet_info *pinfo, proto_tree *tree, 
641                         char *drep, int hf_index)
642 {
643         dcerpc_info *di;
644
645         di=pinfo->private_data;
646         if(di->conformant_run){
647                 /*just a run to handle conformant arrays, nothing to dissect */
648                 return offset;
649         }
650
651         ALIGN_TO_4_BYTES;
652
653         offset = dissect_smb_64bit_time(tvb, pinfo, tree, offset,
654                  hf_index);
655         return offset;
656 }
657