Bugfix.
[metze/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.13 2002/08/28 21:00:09 jmayer 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(len>(guint32)tvb_length_remaining(tvb, offset)){
161                 len=tvb_length_remaining(tvb, offset);
162         }
163
164         if(!pinfo->fd->flags.visited){
165                 mmd=g_mem_chunk_alloc(mapi_decrypted_data_chunk);
166                 mmd->callid=di->call_id;
167                 mmd->frame=pinfo->fd->num;
168                 mmd->data=g_malloc(len);
169                 ptr=(unsigned char *)tvb_get_ptr(tvb, offset, len);
170                 for(i=0;i<len;i++){
171                         mmd->data[i]=ptr[i]^0xa5;
172                 }
173                 mmd->tvb=tvb_new_real_data(mmd->data, len, len);
174                 g_hash_table_insert(mapi_decrypted_table, mmd, mmd);
175         }
176
177         if(!mmd){
178                 mapi_decrypted_data_t mmd_key;
179                 mmd_key.callid=di->call_id;
180                 mmd_key.frame=pinfo->fd->num;
181                 mmd=g_hash_table_lookup(mapi_decrypted_table, &mmd_key);
182         }
183
184         add_new_data_source(pinfo, mmd->tvb, "Decrypted MAPI");
185
186         /* decrypted PDU */
187         /* All from 10 minutes eyeballing. This may be wrong.
188            The PDU is NOT NDR encoded. So this completely new marshalling
189            used by MAPI needs to be figured out.
190
191            It seems that ASCII text strings always are NULL terminated,
192            also no obvious string-length-byte can be seen so it seems the
193            length of strings are determined by searching the terminating null
194            byte.
195
196            The first two bytes of the PDU is the length of the PDU including
197            the two length bytes.
198            The third byte may be a subcommand byte ?
199
200            After the PDU comes, in requests a 4 byte thing. Which is either
201            (not very often) 0xffffffff or something else. If it is
202            'something else' these four bytes are repeated for the matching
203            response packet.
204            In some repsonse packets, this 4 byte trailer are sometimes followed
205            by some other data. Unclear if this is just random padding or actual
206            data. Seems a bit non-random for padding though.
207
208            Some response packets have a PDU of 2 bytes only, ie only the
209            2 byte length field followed by the 4 byte trailer.
210            strange.
211            perhaps the 4 byte trailers, and the extra trailers have some
212            special meaning?
213            More work needs to be done in this area.
214         */
215         it=proto_tree_add_text(tree, mmd->tvb, 0, len, "Decrypted MAPI PDU");
216         tr=proto_item_add_subtree(it, ett_mapi_decrypted_pdu);
217
218         pdu_len=tvb_get_letohs(mmd->tvb, 0);
219         proto_tree_add_uint(tr, hf_mapi_pdu_len, mmd->tvb, 0, 2, pdu_len);
220
221         /*XXX call dissector here */
222         proto_tree_add_item(tr, hf_mapi_decrypted_data, mmd->tvb, 2, pdu_len-2, FALSE);
223
224         proto_tree_add_item(tr, hf_mapi_pdu_trailer, mmd->tvb, pdu_len, 4, FALSE);
225         if(len>((guint32)pdu_len+4)){
226                 proto_tree_add_item(tr, hf_mapi_pdu_extra_trailer, mmd->tvb, pdu_len+4, len-(pdu_len+4), FALSE);
227         }
228
229
230         offset+=len;
231
232         return offset;
233 }
234
235 static int
236 mapi_logon_rqst(tvbuff_t *tvb, int offset,
237         packet_info *pinfo, proto_tree *tree, char *drep)
238 {
239         offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
240                         dissect_ndr_nt_STRING_string, NDR_POINTER_REF,
241                         "unknown string", hf_mapi_unknown_string, -1);
242
243         DISSECT_UNKNOWN(tvb_length_remaining(tvb, offset));
244
245         return offset;
246 }
247
248 /* The strings in this function are decoded properly on seen captures.
249 There might be offsets/padding mismatched due to potential pointer expansions
250 or padding bytes. Captures where this code breaks will tell us about that */
251 static int
252 mapi_logon_reply(tvbuff_t *tvb, int offset,
253         packet_info *pinfo, proto_tree *tree, char *drep)
254 {
255         offset = dissect_nt_policy_hnd(tvb, offset, pinfo, tree, drep,
256                                        hf_mapi_hnd, NULL, FALSE, FALSE);
257
258         DISSECT_UNKNOWN(20); /* this is 20 bytes, unless there are pointers */
259
260         offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
261                         dissect_ndr_nt_STRING_string, NDR_POINTER_REF,
262                         "unknown string", hf_mapi_unknown_string, -1);
263
264         DISSECT_UNKNOWN(6); /* possibly 1 or 2 bytes padding here */
265
266         offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
267                         dissect_ndr_nt_STRING_string, NDR_POINTER_REF,
268                         "unknown string", hf_mapi_unknown_string, -1);
269
270         DISSECT_UNKNOWN( tvb_length_remaining(tvb, offset)-4 );
271
272         offset = dissect_ntstatus(tvb, offset, pinfo, tree, drep,
273                         hf_mapi_rc, NULL);
274
275         return offset;
276 }
277
278 static int
279 mapi_unknown_02_request(tvbuff_t *tvb, int offset,
280         packet_info *pinfo, proto_tree *tree, char *drep)
281 {
282         offset = dissect_nt_policy_hnd(tvb, offset, pinfo, tree, drep,
283                                        hf_mapi_hnd, NULL, FALSE, FALSE);
284
285         if(!mapi_decrypt){
286                 /* this is a unidimensional varying and conformant array of
287                    encrypted data */
288                 offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
289                                 dissect_ndr_nt_STRING_string, NDR_POINTER_REF,
290                                 "unknown data", hf_mapi_unknown_data, -1);
291         } else {
292                 offset = mapi_decrypt_pdu(tvb, offset, pinfo, tree, drep);
293         }
294
295         /* length of encrypted data. */
296         offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
297                         hf_mapi_encap_datalen, NULL);
298
299         offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
300                         hf_mapi_unknown_short, NULL);
301
302         return offset;
303 }
304 static int
305 mapi_unknown_02_reply(tvbuff_t *tvb, int offset,
306         packet_info *pinfo, proto_tree *tree, char *drep)
307 {
308         offset = dissect_nt_policy_hnd(tvb, offset, pinfo, tree, drep,
309                                        hf_mapi_hnd, NULL, FALSE, FALSE);
310
311         if(!mapi_decrypt){
312                 /* this is a unidimensional varying and conformant array of
313                    encrypted data */
314                 offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
315                                 dissect_ndr_nt_STRING_string, NDR_POINTER_REF,
316                                 "unknown data", hf_mapi_unknown_data, -1);
317         } else {
318                 offset = mapi_decrypt_pdu(tvb, offset, pinfo, tree, drep);
319         }
320
321         /* length of encrypted data */
322         offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
323                         hf_mapi_encap_datalen, NULL);
324
325         offset = dissect_ntstatus(tvb, offset, pinfo, tree, drep,
326                         hf_mapi_rc, NULL);
327
328         return offset;
329 }
330
331 static int
332 mapi_logoff_rqst(tvbuff_t *tvb, int offset,
333         packet_info *pinfo, proto_tree *tree, char *drep)
334 {
335         offset = dissect_nt_policy_hnd(tvb, offset, pinfo, tree, drep,
336                                        hf_mapi_hnd, NULL, FALSE, FALSE);
337
338         return offset;
339 }
340
341 static int
342 mapi_logoff_reply(tvbuff_t *tvb, int offset,
343         packet_info *pinfo, proto_tree *tree, char *drep)
344 {
345         offset = dissect_nt_policy_hnd(tvb, offset, pinfo, tree, drep,
346                                        hf_mapi_hnd, NULL, FALSE, FALSE);
347
348         offset = dissect_ntstatus(tvb, offset, pinfo, tree, drep,
349                         hf_mapi_rc, NULL);
350
351         return offset;
352 }
353
354
355 static dcerpc_sub_dissector dcerpc_mapi_dissectors[] = {
356         { MAPI_LOGON,           "Logon",
357                 mapi_logon_rqst,
358                 mapi_logon_reply },
359         { MAPI_LOGOFF,          "Logoff",
360                 mapi_logoff_rqst,
361                 mapi_logoff_reply },
362         { MAPI_UNKNOWN_02,      "unknown_02",
363                 mapi_unknown_02_request,
364                 mapi_unknown_02_reply },
365
366         {0, NULL, NULL,  NULL }
367 };
368
369 void
370 proto_register_dcerpc_mapi(void)
371 {
372
373 static hf_register_info hf[] = {
374         { &hf_mapi_hnd,
375                 { "Context Handle", "mapi.hnd", FT_BYTES, BASE_NONE,
376                 NULL, 0x0, "", HFILL }},
377
378         { &hf_mapi_rc,
379                 { "Return code", "mapi.rc", FT_UINT32, BASE_HEX,
380                 VALS (NT_errors), 0x0, "", HFILL }},
381
382         { &hf_mapi_unknown_string,
383                 { "Unknown string", "mapi.unknown_string", FT_STRING, BASE_NONE,
384                 NULL, 0, "Unknown string. If you know what this is, contact ethereal developers.", HFILL }},
385
386         { &hf_mapi_unknown_short,
387                 { "Unknown short", "mapi.unknown_short", FT_UINT16, BASE_HEX,
388                 NULL, 0, "Unknown short. If you know what this is, contact ethereal developers.", HFILL }},
389
390         { &hf_mapi_unknown_data,
391                 { "unknown encrypted data", "mapi.unknown_data", FT_BYTES, BASE_HEX,
392                 NULL, 0, "Unknown data. If you know what this is, contact ethereal developers.", HFILL }},
393
394         { &hf_mapi_encap_datalen,
395                 { "Length", "mapi.encap_len", FT_UINT16, BASE_DEC,
396                 NULL, 0x0, "Length of encapsulated/encrypted data", HFILL }},
397
398         { &hf_mapi_decrypted_data_maxlen,
399                 { "Max Length", "mapi.decrypted.data.maxlen", FT_UINT32, BASE_DEC,
400                 NULL, 0x0, "Maximum size of buffer for decrypted data", HFILL }},
401
402         { &hf_mapi_decrypted_data_offset,
403                 { "Offset", "mapi.decrypted.data.offset", FT_UINT32, BASE_DEC,
404                 NULL, 0x0, "Offset into buffer for decrypted data", HFILL }},
405
406         { &hf_mapi_decrypted_data_len,
407                 { "Length", "mapi.decrypted.data.len", FT_UINT32, BASE_DEC,
408                 NULL, 0x0, "Used size of buffer for decrypted data", HFILL }},
409
410         { &hf_mapi_decrypted_data,
411                 { "Decrypted data", "mapi.decrypted.data", FT_BYTES, BASE_HEX,
412                 NULL, 0x0, "Decrypted data", HFILL }},
413
414         { &hf_mapi_pdu_len,
415                 { "Length", "mapi.pdu.len", FT_UINT16, BASE_DEC,
416                 NULL, 0x0, "Size of the command PDU", HFILL }},
417
418         { &hf_mapi_pdu_trailer,
419                 { "Trailer", "mapi.pdu.trailer", FT_UINT32, BASE_HEX,
420                 NULL, 0x0, "If you know what this is, contact ethereal developers", HFILL }},
421
422         { &hf_mapi_pdu_extra_trailer,
423                 { "unknown", "mapi.pdu.extra_trailer", FT_BYTES, BASE_HEX,
424                 NULL, 0x0, "If you know what this is, contact ethereal developers", HFILL }}
425         };
426
427
428         static gint *ett[] = {
429                 &ett_dcerpc_mapi,
430                 &ett_mapi_decrypted_pdu
431         };
432         module_t *mapi_module;
433
434         proto_dcerpc_mapi = proto_register_protocol(
435                 "Microsoft Exchange MAPI", "MAPI", "mapi");
436
437         proto_register_field_array(proto_dcerpc_mapi, hf,
438                                    array_length(hf));
439         proto_register_subtree_array(ett, array_length(ett));
440         mapi_module = prefs_register_protocol(proto_dcerpc_mapi, NULL);
441         prefs_register_bool_preference(mapi_module, "mapi_decrypt",
442                 "Decrypt MAPI PDUs",
443                 "Whether the dissector should decrypt MAPI PDUs",
444                 &mapi_decrypt);
445         register_init_routine(mapi_decrypt_init);
446 }
447
448 void
449 proto_reg_handoff_dcerpc_mapi(void)
450 {
451         /* Register protocol as dcerpc */
452
453         dcerpc_init_uuid(proto_dcerpc_mapi, ett_dcerpc_mapi,
454                          &uuid_dcerpc_mapi, ver_dcerpc_mapi,
455                          dcerpc_mapi_dissectors, -1);
456 }