From Todd Sabin: allocate the buffer for the decrypted payload, rather
[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.20 2003/02/10 02:07:15 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_short = -1;
41 static int hf_mapi_hnd = -1;
42 static int hf_mapi_rc = -1;
43 static int hf_mapi_encap_datalen = -1;
44 static int hf_mapi_encrypted_data = -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         const mapi_decrypted_data_t *mdd=(const mapi_decrypted_data_t *)k;
102         return mdd->frame;
103 }
104 static gint
105 mapi_decrypt_equal(gconstpointer k1, gconstpointer k2)
106 {
107         const mapi_decrypted_data_t *mdd1=(const mapi_decrypted_data_t *)k1;
108         const mapi_decrypted_data_t *mdd2=(const 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         const 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=(const 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_cvstring(tvb, offset, pinfo, tree, drep,
240                         sizeof(guint8), hf_mapi_unknown_string, TRUE, NULL);
241
242         DISSECT_UNKNOWN(tvb_length_remaining(tvb, offset));
243
244         return offset;
245 }
246
247 /* The strings in this function are decoded properly on seen captures.
248 There might be offsets/padding mismatched due to potential pointer expansions
249 or padding bytes. Captures where this code breaks will tell us about that */
250 static int
251 mapi_logon_reply(tvbuff_t *tvb, int offset,
252         packet_info *pinfo, proto_tree *tree, char *drep)
253 {
254         offset = dissect_nt_policy_hnd(tvb, offset, pinfo, tree, drep,
255                                        hf_mapi_hnd, NULL, FALSE, FALSE);
256
257         DISSECT_UNKNOWN(20); /* this is 20 bytes, unless there are pointers */
258
259         offset = dissect_ndr_cvstring(tvb, offset, pinfo, tree, drep,
260                         sizeof(guint8), hf_mapi_unknown_string, TRUE, NULL);
261
262         DISSECT_UNKNOWN(6); /* possibly 1 or 2 bytes padding here */
263
264         offset = dissect_ndr_cvstring(tvb, offset, pinfo, tree, drep,
265                         sizeof(guint8), hf_mapi_unknown_string, TRUE, NULL);
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_byte_array, NDR_POINTER_REF,
287                                 "Encrypted data", hf_mapi_encrypted_data);
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_byte_array, NDR_POINTER_REF,
313                                 "Encrypted data", hf_mapi_encrypted_data);
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_encap_datalen,
388                 { "Length", "mapi.encap_len", FT_UINT16, BASE_DEC,
389                 NULL, 0x0, "Length of encapsulated/encrypted data", HFILL }},
390
391         { &hf_mapi_encrypted_data,
392                 { "Encrypted data", "mapi.encrypted_data", FT_BYTES, BASE_HEX,
393                 NULL, 0, "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, "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, -1);
453 }