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