Removed some trailing commas from various DCERPC dissectors.
[obnox/wireshark/wip.git] / packet-dcerpc-mapi.c
1 /* packet-dcerpc-mapi.c
2  * Routines for MS Exchange MAPI
3  * Copyright 2002, Ronnie Sahlberg
4  *
5  * $Id: packet-dcerpc-mapi.c,v 1.9 2002/05/31 00:31:13 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 "packet-dcerpc-mapi.h"
35 #include "smb.h"        /* for "NT_errors[]" */
36 #include "prefs.h"
37
38 static int proto_dcerpc_mapi = -1;
39 static int hf_mapi_unknown_string = -1;
40 static int hf_mapi_unknown_data = -1;
41 static int hf_mapi_unknown_short = -1;
42 static int hf_mapi_hnd = -1;
43 static int hf_mapi_rc = -1;
44 static int hf_mapi_encap_datalen = -1;
45 static int hf_mapi_decrypted_data_maxlen = -1;
46 static int hf_mapi_decrypted_data_offset = -1;
47 static int hf_mapi_decrypted_data_len = -1;
48 static int hf_mapi_decrypted_data = -1;
49 static int hf_mapi_pdu_len = -1;
50 static int hf_mapi_pdu_trailer = -1;
51 static int hf_mapi_pdu_extra_trailer = -1;
52
53 static gint ett_dcerpc_mapi = -1;
54 static gint ett_mapi_decrypted_pdu = -1;
55
56 static e_uuid_t uuid_dcerpc_mapi = {
57         0xa4f1db00, 0xca47, 0x1067,
58         { 0xb3, 0x1f, 0x00, 0xdd, 0x01, 0x06, 0x62, 0xda }
59 };
60
61 static guint16 ver_dcerpc_mapi = 0;
62
63 #define DISSECT_UNKNOWN(len) \
64         {\
65         proto_tree_add_text(tree, tvb, offset, len,\
66                 "unknown data (%d byte%s)", len,\
67                 plurality(len, "", "s"));\
68         offset += len;\
69         }
70
71 /* decryption */
72 static gboolean mapi_decrypt = FALSE;
73 static GMemChunk *mapi_decrypted_data_chunk = NULL;
74 static int mapi_decrypted_data_init_count = 200;
75 static GHashTable *mapi_decrypted_table = NULL;
76 typedef struct {
77         guint32 frame;
78         guint32 callid;
79         tvbuff_t *tvb;
80         unsigned char *data;
81 } mapi_decrypted_data_t;
82
83 static gboolean
84 free_all_decrypted(gpointer key_arg, gpointer value _U_, gpointer user_data _U_)
85 {
86         mapi_decrypted_data_t *mdd=(mapi_decrypted_data_t *)key_arg;
87
88         if(mdd->tvb){
89                 tvb_free(mdd->tvb);
90                 mdd->tvb=NULL;
91         }
92         if(mdd->data){
93                 g_free(mdd->data);
94                 mdd->data=NULL;
95         }
96         return TRUE;
97 }
98 static guint
99 mapi_decrypt_hash(gconstpointer k)
100 {
101         mapi_decrypted_data_t *mdd=(mapi_decrypted_data_t *)k;
102         return mdd->frame;
103 }
104 static gint
105 mapi_decrypt_equal(gconstpointer k1, gconstpointer k2)
106 {
107         mapi_decrypted_data_t *mdd1=(mapi_decrypted_data_t *)k1;
108         mapi_decrypted_data_t *mdd2=(mapi_decrypted_data_t *)k2;
109
110         return  ( (mdd1->frame==mdd2->frame)
111                 &&(mdd1->callid==mdd2->callid) );
112 }
113 static void
114 mapi_decrypt_init(void)
115 {
116         if(mapi_decrypted_table){
117                 g_hash_table_foreach_remove(mapi_decrypted_table,
118                         free_all_decrypted, NULL);
119         } else {
120                 mapi_decrypted_table=g_hash_table_new(mapi_decrypt_hash,
121                         mapi_decrypt_equal);
122         }
123
124         if(mapi_decrypted_data_chunk){
125                 g_mem_chunk_destroy(mapi_decrypted_data_chunk);
126                 mapi_decrypted_data_chunk=NULL;
127         }
128
129         if(mapi_decrypt){
130                 mapi_decrypted_data_chunk=g_mem_chunk_new("mapi_decrypt_chunk",
131                         sizeof(mapi_decrypted_data_t),
132                         mapi_decrypted_data_init_count*sizeof(mapi_decrypted_data_t),
133                         G_ALLOC_ONLY);
134         }
135 }
136
137
138 static int
139 mapi_decrypt_pdu(tvbuff_t *tvb, int offset,
140         packet_info *pinfo, proto_tree *tree, char *drep)
141 {
142         dcerpc_info *di;
143         mapi_decrypted_data_t *mmd=NULL;
144         guint32 len;
145         unsigned char *ptr;
146         guint32 i;
147         guint16 pdu_len;
148         proto_item *it = NULL;
149         proto_tree *tr = NULL;
150
151         di=pinfo->private_data;
152         if(di->conformant_run){
153                 return offset;
154         }
155
156         offset=dissect_ndr_uint32(tvb, offset, pinfo, tree, drep, hf_mapi_decrypted_data_maxlen, NULL);
157         offset=dissect_ndr_uint32(tvb, offset, pinfo, tree, drep, hf_mapi_decrypted_data_offset, NULL);
158         offset=dissect_ndr_uint32(tvb, offset, pinfo, tree, drep, hf_mapi_decrypted_data_len, &len);
159
160         if(!pinfo->fd->flags.visited){
161                 mmd=g_mem_chunk_alloc(mapi_decrypted_data_chunk);
162                 mmd->callid=di->call_id;
163                 mmd->frame=pinfo->fd->num;
164                 mmd->data=g_malloc(len);
165                 ptr=(unsigned char *)tvb_get_ptr(tvb, offset, len);
166                 for(i=0;i<len;i++){
167                         mmd->data[i]=ptr[i]^0xa5;
168                 }
169                 mmd->tvb=tvb_new_real_data(mmd->data, len, len);
170                 g_hash_table_insert(mapi_decrypted_table, mmd, mmd);
171         }
172
173         if(!mmd){
174                 mapi_decrypted_data_t mmd_key;
175                 mmd_key.callid=di->call_id;
176                 mmd_key.frame=pinfo->fd->num;
177                 mmd=g_hash_table_lookup(mapi_decrypted_table, &mmd_key);
178         }
179
180         add_new_data_source(pinfo->fd, mmd->tvb, "Decrypted MAPI");
181
182
183         /* decrypted PDU */
184         /* All from 10 minutes eyeballing. This may be wrong.
185            The PDU is NOT NDR encoded. So this completely new marshalling
186            used by MAPI needs to be figured out.
187
188            It seems that ASCII text strings always are NULL terminated,
189            also no obvious string-length-byte can be seen so it seems the
190            length of strings are determined by searching the terminating null 
191            byte.
192
193            The first two bytes of the PDU is the length of the PDU including
194            the two length bytes.
195            The third byte may be a subcommand byte ?
196
197            After the PDU comes, in requests a 4 byte thing. Which is either 
198            (not very often) 0xffffffff or something else. If it is 
199            'something else' these four bytes are repeated for the matching
200            response packet.
201            In some repsonse packets, this 4 byte trailer are sometimes followed
202            by some other data. Unclear if this is just random padding or actual
203            data. Seems a bit non-random for padding though.
204
205            Some response packets have a PDU of 2 bytes only, ie only the
206            2 byte length field followed by the 4 byte trailer.
207            strange.
208            perhaps the 4 byte trailers, and the extra trailers have some
209            special meaning?
210            More work needs to be done in this area.
211         */
212         it=proto_tree_add_text(tree, mmd->tvb, 0, len, "Decrypted MAPI PDU");
213         tr=proto_item_add_subtree(it, ett_mapi_decrypted_pdu);
214
215         pdu_len=tvb_get_letohs(mmd->tvb, 0);
216         proto_tree_add_uint(tr, hf_mapi_pdu_len, mmd->tvb, 0, 2, pdu_len);
217
218         /*XXX call dissector here */
219         proto_tree_add_item(tr, hf_mapi_decrypted_data, mmd->tvb, 2, pdu_len-2, FALSE);
220
221         proto_tree_add_item(tr, hf_mapi_pdu_trailer, mmd->tvb, pdu_len, 4, FALSE);
222         if(len>((guint32)pdu_len+4)){
223                 proto_tree_add_item(tr, hf_mapi_pdu_extra_trailer, mmd->tvb, pdu_len+4, len-(pdu_len+4), FALSE);
224         }
225
226
227         offset+=len;
228
229         return offset;
230 }
231
232 static int
233 mapi_logon_rqst(tvbuff_t *tvb, int offset,
234         packet_info *pinfo, proto_tree *tree, char *drep)
235 {
236         offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
237                         dissect_ndr_nt_STRING_string, NDR_POINTER_REF,
238                         "unknown string", hf_mapi_unknown_string, -1);
239
240         DISSECT_UNKNOWN(tvb_length_remaining(tvb, offset));
241   
242         return offset;
243 }
244
245 /* The strings in this function are decoded properly on seen captures.
246 There might be offsets/padding mismatched due to potential pointer expansions
247 or padding bytes. Captures where this code breaks will tell us about that */
248 static int
249 mapi_logon_reply(tvbuff_t *tvb, int offset,
250         packet_info *pinfo, proto_tree *tree, char *drep)
251 {
252         offset = dissect_nt_policy_hnd(tvb, offset, pinfo, tree, drep,
253                                        hf_mapi_hnd, NULL, FALSE, FALSE);
254
255         DISSECT_UNKNOWN(20); /* this is 20 bytes, unless there are pointers */
256
257         offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
258                         dissect_ndr_nt_STRING_string, NDR_POINTER_REF,
259                         "unknown string", hf_mapi_unknown_string, -1);
260
261         DISSECT_UNKNOWN(6); /* possibly 1 or 2 bytes padding here */
262
263         offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
264                         dissect_ndr_nt_STRING_string, NDR_POINTER_REF,
265                         "unknown string", hf_mapi_unknown_string, -1);
266
267         DISSECT_UNKNOWN( tvb_length_remaining(tvb, offset)-4 );
268
269         offset = dissect_ntstatus(tvb, offset, pinfo, tree, drep,
270                         hf_mapi_rc, NULL);
271
272         return offset;
273 }
274
275 static int
276 mapi_unknown_02_request(tvbuff_t *tvb, int offset,
277         packet_info *pinfo, proto_tree *tree, char *drep)
278 {
279         offset = dissect_nt_policy_hnd(tvb, offset, pinfo, tree, drep,
280                                        hf_mapi_hnd, NULL, FALSE, FALSE);
281
282         if(!mapi_decrypt){
283                 /* this is a unidimensional varying and conformant array of
284                    encrypted data */  
285                 offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
286                                 dissect_ndr_nt_STRING_string, NDR_POINTER_REF,
287                                 "unknown data", hf_mapi_unknown_data, -1);
288         } else {
289                 offset = mapi_decrypt_pdu(tvb, offset, pinfo, tree, drep);
290         }
291
292         /* length of encrypted data. */
293         offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
294                         hf_mapi_encap_datalen, NULL);
295
296         offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
297                         hf_mapi_unknown_short, NULL);
298
299         return offset;
300 }
301 static int
302 mapi_unknown_02_reply(tvbuff_t *tvb, int offset,
303         packet_info *pinfo, proto_tree *tree, char *drep)
304 {
305         offset = dissect_nt_policy_hnd(tvb, offset, pinfo, tree, drep,
306                                        hf_mapi_hnd, NULL, FALSE, FALSE);
307
308         if(!mapi_decrypt){
309                 /* this is a unidimensional varying and conformant array of
310                    encrypted data */  
311                 offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
312                                 dissect_ndr_nt_STRING_string, NDR_POINTER_REF,
313                                 "unknown data", hf_mapi_unknown_data, -1);
314         } else {
315                 offset = mapi_decrypt_pdu(tvb, offset, pinfo, tree, drep);
316         }
317         
318         /* length of encrypted data */
319         offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
320                         hf_mapi_encap_datalen, NULL);
321
322         offset = dissect_ntstatus(tvb, offset, pinfo, tree, drep,
323                         hf_mapi_rc, NULL);
324
325         return offset;
326 }
327
328 static int
329 mapi_logoff_rqst(tvbuff_t *tvb, int offset,
330         packet_info *pinfo, proto_tree *tree, char *drep)
331 {
332         offset = dissect_nt_policy_hnd(tvb, offset, pinfo, tree, drep,
333                                        hf_mapi_hnd, NULL, FALSE, FALSE);
334
335         return offset;
336 }
337
338 static int
339 mapi_logoff_reply(tvbuff_t *tvb, int offset,
340         packet_info *pinfo, proto_tree *tree, char *drep)
341 {
342         offset = dissect_nt_policy_hnd(tvb, offset, pinfo, tree, drep,
343                                        hf_mapi_hnd, NULL, FALSE, FALSE);
344
345         offset = dissect_ntstatus(tvb, offset, pinfo, tree, drep,
346                         hf_mapi_rc, NULL);
347
348         return offset;
349 }
350
351
352 static dcerpc_sub_dissector dcerpc_mapi_dissectors[] = {
353         { MAPI_LOGON,           "Logon", 
354                 mapi_logon_rqst,
355                 mapi_logon_reply },
356         { MAPI_LOGOFF,          "Logoff", 
357                 mapi_logoff_rqst,
358                 mapi_logoff_reply },
359         { MAPI_UNKNOWN_02,      "unknown_02", 
360                 mapi_unknown_02_request,
361                 mapi_unknown_02_reply },
362
363         {0, NULL, NULL,  NULL }
364 };
365
366 void 
367 proto_register_dcerpc_mapi(void)
368 {
369
370 static hf_register_info hf[] = {
371         { &hf_mapi_hnd,
372                 { "Context Handle", "mapi.hnd", FT_BYTES, BASE_NONE, 
373                 NULL, 0x0, "", HFILL }},
374
375         { &hf_mapi_rc,
376                 { "Return code", "mapi.rc", FT_UINT32, BASE_HEX, 
377                 VALS (NT_errors), 0x0, "", HFILL }},
378
379         { &hf_mapi_unknown_string,
380                 { "Unknown string", "mapi.unknown_string", FT_STRING, BASE_NONE,
381                 NULL, 0, "Unknown string. If you know what this is, contact ethereal developers.", HFILL }},
382
383         { &hf_mapi_unknown_short,
384                 { "Unknown short", "mapi.unknown_short", FT_UINT16, BASE_HEX,
385                 NULL, 0, "Unknown short. If you know what this is, contact ethereal developers.", HFILL }},
386
387         { &hf_mapi_unknown_data,
388                 { "unknown encrypted data", "mapi.unknown_data", FT_BYTES, BASE_HEX,
389                 NULL, 0, "Unknown data. If you know what this is, contact ethereal developers.", HFILL }},
390
391         { &hf_mapi_encap_datalen,
392                 { "Length", "mapi.encap_len", FT_UINT16, BASE_DEC, 
393                 NULL, 0x0, "Length of encapsulated/encrypted data", HFILL }},
394
395         { &hf_mapi_decrypted_data_maxlen,
396                 { "Max Length", "mapi.decrypted.data.maxlen", FT_UINT32, BASE_DEC, 
397                 NULL, 0x0, "Maximum size of buffer for decrypted data", HFILL }},
398
399         { &hf_mapi_decrypted_data_offset,
400                 { "Offset", "mapi.decrypted.data.offset", FT_UINT32, BASE_DEC, 
401                 NULL, 0x0, "Offset into buffer for decrypted data", HFILL }},
402
403         { &hf_mapi_decrypted_data_len,
404                 { "Length", "mapi.decrypted.data.len", FT_UINT32, BASE_DEC, 
405                 NULL, 0x0, "Used size of buffer for decrypted data", HFILL }},
406
407         { &hf_mapi_decrypted_data,
408                 { "Decrypted data", "mapi.decrypted.data", FT_BYTES, BASE_HEX, 
409                 NULL, 0x0, "Decrypted data", HFILL }},
410
411         { &hf_mapi_pdu_len,
412                 { "Length", "mapi.pdu.len", FT_UINT16, BASE_DEC, 
413                 NULL, 0x0, "Size of the command PDU", HFILL }},
414
415         { &hf_mapi_pdu_trailer,
416                 { "Trailer", "mapi.pdu.trailer", FT_UINT32, BASE_HEX, 
417                 NULL, 0x0, "If you know what this is, contact ethereal developers", HFILL }},
418
419         { &hf_mapi_pdu_extra_trailer,
420                 { "unknown", "mapi.pdu.extra_trailer", FT_BYTES, BASE_HEX, 
421                 NULL, 0x0, "If you know what this is, contact ethereal developers", HFILL }}
422         };
423
424
425         static gint *ett[] = {
426                 &ett_dcerpc_mapi,
427                 &ett_mapi_decrypted_pdu
428         };
429         module_t *mapi_module;
430
431         proto_dcerpc_mapi = proto_register_protocol(
432                 "Microsoft Exchange MAPI", "MAPI", "mapi");
433
434         proto_register_field_array(proto_dcerpc_mapi, hf, 
435                                    array_length(hf));
436         proto_register_subtree_array(ett, array_length(ett));
437         mapi_module = prefs_register_protocol(proto_dcerpc_mapi, NULL);
438         prefs_register_bool_preference(mapi_module, "mapi_decrypt",
439                 "Decrypt MAPI PDUs",
440                 "Whether the dissector should decrypt MAPI PDUs",
441                 &mapi_decrypt);
442         register_init_routine(mapi_decrypt_init);
443 }
444
445 void
446 proto_reg_handoff_dcerpc_mapi(void)
447 {
448         /* Register protocol as dcerpc */
449
450         dcerpc_init_uuid(proto_dcerpc_mapi, ett_dcerpc_mapi, 
451                          &uuid_dcerpc_mapi, ver_dcerpc_mapi, 
452                          dcerpc_mapi_dissectors);
453 }