1 /* packet-dcerpc-mapi.c
2 * Routines for MS Exchange MAPI
3 * Copyright 2002, Ronnie Sahlberg
5 * $Id: packet-dcerpc-mapi.c,v 1.20 2003/02/10 02:07:15 tpot Exp $
7 * Ethereal - Network traffic analyzer
8 * By Gerald Combs <gerald@ethereal.com>
9 * Copyright 1998 Gerald Combs
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.
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.
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.
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[]" */
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;
53 static gint ett_dcerpc_mapi = -1;
54 static gint ett_mapi_decrypted_pdu = -1;
56 static e_uuid_t uuid_dcerpc_mapi = {
57 0xa4f1db00, 0xca47, 0x1067,
58 { 0xb3, 0x1f, 0x00, 0xdd, 0x01, 0x06, 0x62, 0xda }
61 static guint16 ver_dcerpc_mapi = 0;
63 #define DISSECT_UNKNOWN(len) \
65 proto_tree_add_text(tree, tvb, offset, len,\
66 "unknown data (%d byte%s)", len,\
67 plurality(len, "", "s"));\
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;
81 } mapi_decrypted_data_t;
84 free_all_decrypted(gpointer key_arg, gpointer value _U_, gpointer user_data _U_)
86 mapi_decrypted_data_t *mdd=(mapi_decrypted_data_t *)key_arg;
99 mapi_decrypt_hash(gconstpointer k)
101 const mapi_decrypted_data_t *mdd=(const mapi_decrypted_data_t *)k;
105 mapi_decrypt_equal(gconstpointer k1, gconstpointer k2)
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;
110 return ( (mdd1->frame==mdd2->frame)
111 &&(mdd1->callid==mdd2->callid) );
114 mapi_decrypt_init(void)
116 if(mapi_decrypted_table){
117 g_hash_table_foreach_remove(mapi_decrypted_table,
118 free_all_decrypted, NULL);
120 mapi_decrypted_table=g_hash_table_new(mapi_decrypt_hash,
124 if(mapi_decrypted_data_chunk){
125 g_mem_chunk_destroy(mapi_decrypted_data_chunk);
126 mapi_decrypted_data_chunk=NULL;
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),
139 mapi_decrypt_pdu(tvbuff_t *tvb, int offset,
140 packet_info *pinfo, proto_tree *tree, char *drep)
143 mapi_decrypted_data_t *mmd=NULL;
145 const unsigned char *ptr;
148 proto_item *it = NULL;
149 proto_tree *tr = NULL;
151 di=pinfo->private_data;
152 if(di->conformant_run){
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);
160 if(len>(guint32)tvb_length_remaining(tvb, offset)){
161 len=tvb_length_remaining(tvb, offset);
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);
171 mmd->data[i]=ptr[i]^0xa5;
173 mmd->tvb=tvb_new_real_data(mmd->data, len, len);
174 g_hash_table_insert(mapi_decrypted_table, mmd, 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);
184 add_new_data_source(pinfo, mmd->tvb, "Decrypted MAPI");
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.
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
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 ?
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
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.
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.
211 perhaps the 4 byte trailers, and the extra trailers have some
213 More work needs to be done in this area.
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);
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);
221 /*XXX call dissector here */
222 proto_tree_add_item(tr, hf_mapi_decrypted_data, mmd->tvb, 2, pdu_len-2, FALSE);
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);
236 mapi_logon_rqst(tvbuff_t *tvb, int offset,
237 packet_info *pinfo, proto_tree *tree, char *drep)
239 offset = dissect_ndr_cvstring(tvb, offset, pinfo, tree, drep,
240 sizeof(guint8), hf_mapi_unknown_string, TRUE, NULL);
242 DISSECT_UNKNOWN(tvb_length_remaining(tvb, offset));
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 */
251 mapi_logon_reply(tvbuff_t *tvb, int offset,
252 packet_info *pinfo, proto_tree *tree, char *drep)
254 offset = dissect_nt_policy_hnd(tvb, offset, pinfo, tree, drep,
255 hf_mapi_hnd, NULL, FALSE, FALSE);
257 DISSECT_UNKNOWN(20); /* this is 20 bytes, unless there are pointers */
259 offset = dissect_ndr_cvstring(tvb, offset, pinfo, tree, drep,
260 sizeof(guint8), hf_mapi_unknown_string, TRUE, NULL);
262 DISSECT_UNKNOWN(6); /* possibly 1 or 2 bytes padding here */
264 offset = dissect_ndr_cvstring(tvb, offset, pinfo, tree, drep,
265 sizeof(guint8), hf_mapi_unknown_string, TRUE, NULL);
267 DISSECT_UNKNOWN( tvb_length_remaining(tvb, offset)-4 );
269 offset = dissect_ntstatus(tvb, offset, pinfo, tree, drep,
276 mapi_unknown_02_request(tvbuff_t *tvb, int offset,
277 packet_info *pinfo, proto_tree *tree, char *drep)
279 offset = dissect_nt_policy_hnd(tvb, offset, pinfo, tree, drep,
280 hf_mapi_hnd, NULL, FALSE, FALSE);
283 /* this is a unidimensional varying and conformant array of
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);
289 offset = mapi_decrypt_pdu(tvb, offset, pinfo, tree, drep);
292 /* length of encrypted data. */
293 offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
294 hf_mapi_encap_datalen, NULL);
296 offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
297 hf_mapi_unknown_short, NULL);
302 mapi_unknown_02_reply(tvbuff_t *tvb, int offset,
303 packet_info *pinfo, proto_tree *tree, char *drep)
305 offset = dissect_nt_policy_hnd(tvb, offset, pinfo, tree, drep,
306 hf_mapi_hnd, NULL, FALSE, FALSE);
309 /* this is a unidimensional varying and conformant array of
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);
315 offset = mapi_decrypt_pdu(tvb, offset, pinfo, tree, drep);
318 /* length of encrypted data */
319 offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
320 hf_mapi_encap_datalen, NULL);
322 offset = dissect_ntstatus(tvb, offset, pinfo, tree, drep,
329 mapi_logoff_rqst(tvbuff_t *tvb, int offset,
330 packet_info *pinfo, proto_tree *tree, char *drep)
332 offset = dissect_nt_policy_hnd(tvb, offset, pinfo, tree, drep,
333 hf_mapi_hnd, NULL, FALSE, FALSE);
339 mapi_logoff_reply(tvbuff_t *tvb, int offset,
340 packet_info *pinfo, proto_tree *tree, char *drep)
342 offset = dissect_nt_policy_hnd(tvb, offset, pinfo, tree, drep,
343 hf_mapi_hnd, NULL, FALSE, FALSE);
345 offset = dissect_ntstatus(tvb, offset, pinfo, tree, drep,
352 static dcerpc_sub_dissector dcerpc_mapi_dissectors[] = {
353 { MAPI_LOGON, "Logon",
356 { MAPI_LOGOFF, "Logoff",
359 { MAPI_UNKNOWN_02, "unknown_02",
360 mapi_unknown_02_request,
361 mapi_unknown_02_reply },
363 {0, NULL, NULL, NULL }
367 proto_register_dcerpc_mapi(void)
370 static hf_register_info hf[] = {
372 { "Context Handle", "mapi.hnd", FT_BYTES, BASE_NONE,
373 NULL, 0x0, "", HFILL }},
376 { "Return code", "mapi.rc", FT_UINT32, BASE_HEX,
377 VALS (NT_errors), 0x0, "", HFILL }},
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 }},
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 }},
387 { &hf_mapi_encap_datalen,
388 { "Length", "mapi.encap_len", FT_UINT16, BASE_DEC,
389 NULL, 0x0, "Length of encapsulated/encrypted data", HFILL }},
391 { &hf_mapi_encrypted_data,
392 { "Encrypted data", "mapi.encrypted_data", FT_BYTES, BASE_HEX,
393 NULL, 0, "Encrypted data", HFILL }},
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 }},
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 }},
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 }},
407 { &hf_mapi_decrypted_data,
408 { "Decrypted data", "mapi.decrypted.data", FT_BYTES, BASE_HEX,
409 NULL, 0x0, "Decrypted data", HFILL }},
412 { "Length", "mapi.pdu.len", FT_UINT16, BASE_DEC,
413 NULL, 0x0, "Size of the command PDU", HFILL }},
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 }},
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 }}
425 static gint *ett[] = {
427 &ett_mapi_decrypted_pdu
429 module_t *mapi_module;
431 proto_dcerpc_mapi = proto_register_protocol(
432 "Microsoft Exchange MAPI", "MAPI", "mapi");
434 proto_register_field_array(proto_dcerpc_mapi, 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",
440 "Whether the dissector should decrypt MAPI PDUs",
442 register_init_routine(mapi_decrypt_init);
446 proto_reg_handoff_dcerpc_mapi(void)
448 /* Register protocol as dcerpc */
450 dcerpc_init_uuid(proto_dcerpc_mapi, ett_dcerpc_mapi,
451 &uuid_dcerpc_mapi, ver_dcerpc_mapi,
452 dcerpc_mapi_dissectors, -1);