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