Moved dissect_ndr_nt_NTTIME() from packet-dcerpc-samr.c to packet-dcerpc-nt.c
[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.12 2002/03/10 23:24:48 sahlberg 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: %d", 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, guint8 **data, char *name)
76 {
77         const guint8 *ptr;
78         
79         /* The tvb_get_ptr() function fails an assertion if count < -1 */
80         
81         if (count < -1)
82                 THROW(BoundsError);
83
84         /* No alignment required */
85
86         ptr = tvb_get_ptr(tvb, offset, count);
87
88         if (name && tree)
89                 proto_tree_add_text(tree, tvb, offset, count, "%s", name);
90
91         if (data)
92                 *data = (guint8 *)ptr;
93
94         offset += count;
95
96         return offset;
97 }
98
99 /* Parse a 16-bit integer */
100
101 int prs_uint16(tvbuff_t *tvb, int offset, packet_info *pinfo,
102                proto_tree *tree, guint16 *data, char *name)
103 {
104         guint16 i;
105         
106         offset = prs_align(offset, 2);
107         
108         i = tvb_get_letohs(tvb, offset);
109         offset += 2;
110
111         if (name && tree)
112                 proto_tree_add_text(tree, tvb, offset - 2, 2, 
113                                     "%s: %d", name, i);
114         if (data)
115                 *data = i;
116
117         return offset;
118 }
119
120 /* Parse a number of uint16's */
121
122 int prs_uint16s(tvbuff_t *tvb, int offset, packet_info *pinfo,
123                 proto_tree *tree, int count, guint16 **data, char *name)
124 {
125         const guint8 *ptr;
126         
127         /* The tvb_get_ptr() function fails an assertion if count < -1 */
128         
129         if (count < -1)
130                 THROW(BoundsError);
131
132         offset = prs_align(offset, 2);
133         
134         ptr = tvb_get_ptr(tvb, offset, count * 2);
135
136         if (name && tree)
137                 proto_tree_add_text(tree, tvb, offset, count * 2, 
138                                     "%s", name);
139         if (data)
140                 *data = (guint16 *)ptr;
141
142         offset += count * 2;
143
144         return offset;
145 }
146
147 /* Parse a 32-bit integer */
148
149 int prs_uint32(tvbuff_t *tvb, int offset, packet_info *pinfo,
150                proto_tree *tree, guint32 *data, char *name)
151 {
152         guint32 i;
153         
154         offset = prs_align(offset, 4);
155         
156         i = tvb_get_letohl(tvb, offset);
157         offset += 4;
158
159         if (name && tree)
160                 proto_tree_add_text(tree, tvb, offset - 4, 4, 
161                                     "%s: %d", name, i);
162
163         if (data)
164                 *data = i;
165
166         return offset;
167 }
168
169 /* Parse a number of 32-bit integers */
170
171 int prs_uint32s(tvbuff_t *tvb, int offset, packet_info *pinfo,
172                 proto_tree *tree, int count, guint32 **data, char *name)
173 {
174         const guint8 *ptr;
175         
176         /* The tvb_get_ptr() function fails an assertion if count < -1 */
177         
178         if (count < -1)
179                 THROW(BoundsError);
180
181         offset = prs_align(offset, 4);
182         
183         ptr = tvb_get_ptr(tvb, offset, count * 4);
184
185         if (name && tree)
186                 proto_tree_add_text(tree, tvb, offset - 4, 4, 
187                                     "%s", name);
188         if (data)
189                 *data = (guint32 *)ptr;
190
191         offset += count * 4;
192
193         return offset;
194 }
195
196 /* Parse a NT status code */
197
198 int prs_ntstatus(tvbuff_t *tvb, int offset, packet_info *pinfo,
199                  proto_tree *tree)
200 {
201         guint32 status;
202
203         offset = prs_uint32(tvb, offset, pinfo, tree, &status, NULL);
204
205         if (tree)
206                 proto_tree_add_text(tree, tvb, offset - 4, 4, "Status: %s",
207                                     val_to_str(status, NT_errors, "???"));
208
209         return offset;
210 }
211
212 /*
213  * We need to keep track of deferred referrents as they appear in the
214  * packet after all the non-pointer objects.
215  * to keep track of pointers as they are parsed as scalars and need to be
216  * remembered for the next call to the prs function.
217  *
218  * Pointers are stored in a linked list and pushed in the PARSE_SCALARS
219  * section of the prs function and popped in the PARSE_BUFFERS section.  If
220  * we try to pop off a referrent that has a different name then we are
221  * expecting then something has gone wrong.
222  */
223
224 #undef DEBUG_PTRS
225
226 struct ptr {
227         char *name;
228         guint32 value;
229 };
230
231 /* Create a new pointer */
232
233 static struct ptr *new_ptr(char *name, guint32 value)
234 {
235         struct ptr *p;
236
237         p = g_malloc(sizeof(struct ptr));
238
239         p->name = g_strdup(name);
240         p->value = value;
241
242         return p;
243 }
244
245 /* Free a pointer */
246
247 static void free_ptr(struct ptr *p)
248 {
249         if (p) {
250                 g_free(p->name);
251                 g_free(p);
252         }
253 }
254
255 /* Parse a pointer and store it's value in a linked list */
256
257 int prs_push_ptr(tvbuff_t *tvb, int offset, packet_info *pinfo,
258                  proto_tree *tree, GList **ptr_list, char *name)
259 {
260         struct ptr *p;
261         guint32 value;
262
263         offset = prs_uint32(tvb, offset, pinfo, tree, &value, NULL);
264
265         if (name && tree)
266                 proto_tree_add_text(tree, tvb, offset - 4, 4, 
267                                     "%s pointer: 0x%08x", name, value);
268
269         p = new_ptr(name, value);
270
271         *ptr_list = g_list_append(*ptr_list, p);
272
273 #ifdef DEBUG_PTRS
274         fprintf(stderr, "DEBUG_PTRS: pushing %s ptr = 0x%08x, %d ptrs in "
275                 "list\n", name, value, g_list_length(*ptr_list));
276 #endif
277
278         return offset;
279 }
280
281 /* Pop a pointer of a given name.  Return it's value. */
282
283 guint32 prs_pop_ptr(GList **ptr_list, char *name)
284 {
285         GList *elt;
286         struct ptr *p;
287         guint32 result;
288
289         g_assert(g_list_length(*ptr_list) != 0);        /* List too short */
290
291         /* Get pointer at head of list */
292
293         elt = g_list_first(*ptr_list);
294         p = (struct ptr *)elt->data;
295         result = p->value;
296
297 #ifdef DEBUG_PTRS
298         if (strcmp(p->name, name) != 0) {
299                 fprintf(stderr, "DEBUG_PTRS: wrong pointer (%s != %s)\n",
300                         p->name, name);
301         }
302 #endif
303
304         /* Free pointer record */
305
306         *ptr_list = g_list_remove_link(*ptr_list, elt);
307
308 #ifdef DEBUG_PTRS
309         fprintf(stderr, "DEBUG_PTRS: popping %s ptr = 0x%08x, %d ptrs in "
310                 "list\n", p->name, p->value, g_list_length(*ptr_list));
311 #endif
312
313         free_ptr(p);
314
315         return result;
316 }
317
318 /*
319  * Parse a UNISTR2 structure 
320  *
321  * typedef struct {
322  *   short length;
323  *   short size;
324  *   [size_is(size/2)] [length_is(length/2)] [unique] wchar_t *string;
325  * } UNICODE_STRING;
326  *
327  */
328
329 /* Convert a string from little-endian unicode to ascii.  At the moment we
330    fake it by taking every odd byte.  )-:  The caller must free the
331    result returned. */
332
333 char *fake_unicode(guint16 *data, int len)
334 {
335         char *buffer;
336         int i;
337
338         buffer = malloc(len + 1);
339
340         for (i = 0; i < len; i++)
341                 buffer[i] = data[i] & 0xff;
342
343         buffer[len] = 0;
344
345         return buffer;
346 }
347
348 /* Parse a UNISTR2 structure */
349
350 int prs_UNISTR2(tvbuff_t *tvb, int offset, packet_info *pinfo,
351                 proto_tree *tree, int flags, char **data, char *name)
352 {
353         guint32 len = 0, unknown = 0, max_len = 0;
354
355         if (flags & PARSE_SCALARS) {
356                 offset = prs_uint32(tvb, offset, pinfo, tree, &len, "Length");
357                 offset = prs_uint32(tvb, offset, pinfo, tree, &unknown, 
358                                     "Offset");
359                 offset = prs_uint32(tvb, offset, pinfo, tree, &max_len, 
360                                     "Max length");
361         }
362
363         if (flags & PARSE_BUFFERS) {
364                 guint16 *data16;
365
366                 offset = prs_uint16s(tvb, offset, pinfo, tree, max_len,
367                                      &data16, "Buffer");
368
369                 if (data)
370                         *data = fake_unicode(data16, max_len);
371         }
372
373         return offset;
374 }
375
376 /* Parse a policy handle. */
377
378 int prs_policy_hnd(tvbuff_t *tvb, int offset, packet_info *pinfo, 
379                    proto_tree *tree, const guint8 **data)
380 {
381         const guint8 *data8;
382
383         offset = prs_align(offset, 4);
384
385         proto_tree_add_text(tree, tvb, offset, 20, "Policy Handle");
386
387         data8 = tvb_get_ptr(tvb, offset, 20);
388         
389         if (data)
390                 *data = data8;
391
392         return offset + 20;
393 }
394
395
396
397 /* following are a few functions for dissecting common structures used by NT 
398    services. These might need to be cleaned up at a later time but at least we get
399    them out of the real service dissectors.
400 */
401
402
403 /* UNICODE_STRING  BEGIN */
404 /* functions to dissect a UNICODE_STRING structure, common to many 
405    NT services
406    struct {
407      short len;
408      short size;
409      [size_is(size/2), length_is(len/2), ptr] unsigned short *string;
410    } UNICODE_STRING;
411
412    these variables can be found in packet-dcerpc-samr.c 
413 */
414 extern int hf_nt_str_len;
415 extern int hf_nt_str_off;
416 extern int hf_nt_str_max_len;
417 extern int hf_nt_string_length;
418 extern int hf_nt_string_size;
419 extern gint ett_nt_unicode_string;
420
421
422 /* this function will dissect the
423      [size_is(size/2), length_is(len/2), ptr] unsigned short *string;
424   part of the unicode string
425
426    struct {
427      short len;
428      short size;
429      [size_is(size/2), length_is(len/2), ptr] unsigned short *string;
430    } UNICODE_STRING;
431   structure used by NT to transmit unicode string values.
432
433   This function also looks at di->levels to see if whoever called us wanted us to append
434   the name: string to any higher levels in the tree .
435 */
436 int
437 dissect_ndr_nt_UNICODE_STRING_str(tvbuff_t *tvb, int offset, 
438                         packet_info *pinfo, proto_tree *tree, 
439                         char *drep)
440 {
441         guint32 len, off, max_len;
442         guint16 *data16;
443         char *text;
444         int old_offset;
445         dcerpc_info *di;
446
447         di=pinfo->private_data;
448         if(di->conformant_run){
449                 /*just a run to handle conformant arrays, nothing to dissect */
450                 return offset;
451         }
452
453         offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
454                         hf_nt_str_len, &len);
455         offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
456                         hf_nt_str_off, &off);
457         offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
458                         hf_nt_str_max_len, &max_len);
459
460         old_offset=offset;
461         offset = prs_uint16s(tvb, offset, pinfo, tree, max_len, &data16, NULL);
462         text = fake_unicode(data16, max_len);
463
464         proto_tree_add_string(tree, di->hf_index, tvb, old_offset,
465                 offset-old_offset, text);
466
467         if(tree){
468                 proto_item_append_text(tree, ": %s", text);
469                 if(di->levels>-1){
470                         tree=tree->parent;
471                         proto_item_append_text(tree, ": %s", text);
472                         while(di->levels>0){
473                                 tree=tree->parent;
474                                 proto_item_append_text(tree, " %s", text);
475                                 di->levels--;
476                         }
477                 }
478         }
479         return offset;
480 }
481
482 /* this function will dissect the
483    struct {
484      short len;
485      short size;
486      [size_is(size/2), length_is(len/2), ptr] unsigned short *string;
487    } UNICODE_STRING;
488   structure used by NT to transmit unicode string values.
489  
490   the function takes one additional parameter, level
491   which specifies how many additional levels up in the tree where we should
492   append the string.  If unsure, specify levels as 0.
493 */
494 int
495 dissect_ndr_nt_UNICODE_STRING(tvbuff_t *tvb, int offset, 
496                         packet_info *pinfo, proto_tree *parent_tree, 
497                         char *drep, int hf_index, int levels)
498 {
499         proto_item *item=NULL;
500         proto_tree *tree=NULL;
501         int old_offset=offset;
502         dcerpc_info *di;
503         char *name;
504
505         ALIGN_TO_4_BYTES;  /* strcture starts with short, but is aligned for longs */
506
507         di=pinfo->private_data;
508         if(di->conformant_run){
509                 /*just a run to handle conformant arrays, nothing to dissect */
510                 return offset;
511         }
512
513         name = proto_registrar_get_name(hf_index);
514         if(parent_tree){
515                 item = proto_tree_add_text(parent_tree, tvb, offset, -1,
516                         "%s", name);
517                 tree = proto_item_add_subtree(item, ett_nt_unicode_string);
518         }
519
520         offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
521                         hf_nt_string_length, NULL);
522         offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
523                         hf_nt_string_size, NULL);
524         di->levels=1;
525         offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
526                         dissect_ndr_nt_UNICODE_STRING_str, NDR_POINTER_UNIQUE,
527                         name, hf_index, levels);
528
529         proto_item_set_len(item, offset-old_offset);
530         return offset;
531 }
532 /* UNICODE_STRING  END */
533
534
535 /* This function is used to dissect a DCERPC encoded 64 bit time value.
536    XXX it should be fixed both here and in dissect_smb_64bit_time so
537    it can handle both BIG and LITTLE endian encodings 
538  */
539 int
540 dissect_ndr_nt_NTTIME (tvbuff_t *tvb, int offset, 
541                         packet_info *pinfo, proto_tree *tree, 
542                         char *drep, int hf_index)
543 {
544         dcerpc_info *di;
545
546         di=pinfo->private_data;
547         if(di->conformant_run){
548                 /*just a run to handle conformant arrays, nothing to dissect */
549                 return offset;
550         }
551
552         ALIGN_TO_4_BYTES;
553
554         offset = dissect_smb_64bit_time(tvb, pinfo, tree, offset,
555                  hf_index);
556         return offset;
557 }
558