1 /* packet-dcerpc-mapi.c
2 * Routines for MS Exchange MAPI
3 * Copyright 2002, Ronnie Sahlberg
5 * $Id: packet-dcerpc-mapi.c,v 1.24 2003/08/04 02:49:02 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_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;
55 static gint ett_dcerpc_mapi = -1;
56 static gint ett_mapi_decrypted_pdu = -1;
58 static e_uuid_t uuid_dcerpc_mapi = {
59 0xa4f1db00, 0xca47, 0x1067,
60 { 0xb3, 0x1f, 0x00, 0xdd, 0x01, 0x06, 0x62, 0xda }
63 static guint16 ver_dcerpc_mapi = 0;
65 #define DISSECT_UNKNOWN(len) \
67 proto_tree_add_text(tree, tvb, offset, len,\
68 "unknown data (%d byte%s)", len,\
69 plurality(len, "", "s"));\
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;
83 } mapi_decrypted_data_t;
86 free_all_decrypted(gpointer key_arg, gpointer value _U_, gpointer user_data _U_)
88 mapi_decrypted_data_t *mdd=(mapi_decrypted_data_t *)key_arg;
101 mapi_decrypt_hash(gconstpointer k)
103 const mapi_decrypted_data_t *mdd=(const mapi_decrypted_data_t *)k;
107 mapi_decrypt_equal(gconstpointer k1, gconstpointer k2)
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;
112 return ( (mdd1->frame==mdd2->frame)
113 &&(mdd1->callid==mdd2->callid) );
116 mapi_decrypt_init(void)
118 if(mapi_decrypted_table){
119 g_hash_table_foreach_remove(mapi_decrypted_table,
120 free_all_decrypted, NULL);
122 mapi_decrypted_table=g_hash_table_new(mapi_decrypt_hash,
126 if(mapi_decrypted_data_chunk){
127 g_mem_chunk_destroy(mapi_decrypted_data_chunk);
128 mapi_decrypted_data_chunk=NULL;
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),
141 mapi_decrypt_pdu(tvbuff_t *tvb, int offset,
142 packet_info *pinfo, proto_tree *tree, char *drep)
145 mapi_decrypted_data_t *mmd=NULL;
147 const unsigned char *ptr;
150 proto_item *it = NULL;
151 proto_tree *tr = NULL;
153 di=pinfo->private_data;
154 if(di->conformant_run){
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);
162 if(len>(guint32)tvb_length_remaining(tvb, offset)){
163 len=tvb_length_remaining(tvb, offset);
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);
173 mmd->data[i]=ptr[i]^0xa5;
175 mmd->tvb=tvb_new_real_data(mmd->data, len, len);
176 g_hash_table_insert(mapi_decrypted_table, mmd, 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);
186 add_new_data_source(pinfo, mmd->tvb, "Decrypted MAPI");
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.
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
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 ?
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
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.
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.
213 perhaps the 4 byte trailers, and the extra trailers have some
215 More work needs to be done in this area.
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);
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);
223 /*XXX call dissector here */
224 proto_tree_add_item(tr, hf_mapi_decrypted_data, mmd->tvb, 2, pdu_len-2, FALSE);
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);
238 mapi_logon_rqst(tvbuff_t *tvb, int offset,
239 packet_info *pinfo, proto_tree *tree, char *drep)
241 offset = dissect_ndr_cvstring(tvb, offset, pinfo, tree, drep,
242 sizeof(guint8), hf_mapi_unknown_string, TRUE, NULL);
244 DISSECT_UNKNOWN(tvb_length_remaining(tvb, offset));
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 */
253 mapi_logon_reply(tvbuff_t *tvb, int offset,
254 packet_info *pinfo, proto_tree *tree, char *drep)
256 offset = dissect_nt_policy_hnd(tvb, offset, pinfo, tree, drep,
257 hf_mapi_hnd, NULL, NULL, FALSE, FALSE);
259 DISSECT_UNKNOWN(20); /* this is 20 bytes, unless there are pointers */
261 offset = dissect_ndr_cvstring(tvb, offset, pinfo, tree, drep,
262 sizeof(guint8), hf_mapi_unknown_string, TRUE, NULL);
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);
271 offset = dissect_ndr_cvstring(tvb, offset, pinfo, tree, drep,
272 sizeof(guint8), hf_mapi_unknown_string, TRUE, NULL);
274 DISSECT_UNKNOWN( tvb_length_remaining(tvb, offset)-4 );
276 offset = dissect_ntstatus(tvb, offset, pinfo, tree, drep,
283 mapi_unknown_02_request(tvbuff_t *tvb, int offset,
284 packet_info *pinfo, proto_tree *tree, char *drep)
286 offset = dissect_nt_policy_hnd(tvb, offset, pinfo, tree, drep,
287 hf_mapi_hnd, NULL, NULL, FALSE, FALSE);
290 /* this is a unidimensional varying and conformant array of
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);
296 offset = mapi_decrypt_pdu(tvb, offset, pinfo, tree, drep);
299 /* length of encrypted data. */
300 offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
301 hf_mapi_encap_datalen, NULL);
303 offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
304 hf_mapi_unknown_short, NULL);
309 mapi_unknown_02_reply(tvbuff_t *tvb, int offset,
310 packet_info *pinfo, proto_tree *tree, char *drep)
312 offset = dissect_nt_policy_hnd(tvb, offset, pinfo, tree, drep,
313 hf_mapi_hnd, NULL, NULL, FALSE, FALSE);
316 /* this is a unidimensional varying and conformant array of
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);
322 offset = mapi_decrypt_pdu(tvb, offset, pinfo, tree, drep);
325 /* length of encrypted data */
326 offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
327 hf_mapi_encap_datalen, NULL);
329 offset = dissect_ntstatus(tvb, offset, pinfo, tree, drep,
336 mapi_logoff_rqst(tvbuff_t *tvb, int offset,
337 packet_info *pinfo, proto_tree *tree, char *drep)
339 offset = dissect_nt_policy_hnd(tvb, offset, pinfo, tree, drep,
340 hf_mapi_hnd, NULL, NULL, FALSE, FALSE);
346 mapi_logoff_reply(tvbuff_t *tvb, int offset,
347 packet_info *pinfo, proto_tree *tree, char *drep)
349 offset = dissect_nt_policy_hnd(tvb, offset, pinfo, tree, drep,
350 hf_mapi_hnd, NULL, NULL, FALSE, FALSE);
352 offset = dissect_ntstatus(tvb, offset, pinfo, tree, drep,
359 static dcerpc_sub_dissector dcerpc_mapi_dissectors[] = {
360 { MAPI_LOGON, "Logon",
363 { MAPI_LOGOFF, "Logoff",
366 { MAPI_UNKNOWN_02, "unknown_02",
367 mapi_unknown_02_request,
368 mapi_unknown_02_reply },
370 {0, NULL, NULL, NULL }
374 proto_register_dcerpc_mapi(void)
377 static hf_register_info hf[] = {
379 { "Operation", "mapi.opnum", FT_UINT16, BASE_DEC,
380 NULL, 0x0, "", HFILL }},
383 { "Context Handle", "mapi.hnd", FT_BYTES, BASE_NONE,
384 NULL, 0x0, "", HFILL }},
387 { "Return code", "mapi.rc", FT_UINT32, BASE_HEX,
388 VALS (NT_errors), 0x0, "", HFILL }},
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 }},
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 }},
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 }},
402 { &hf_mapi_encap_datalen,
403 { "Length", "mapi.encap_len", FT_UINT16, BASE_DEC,
404 NULL, 0x0, "Length of encapsulated/encrypted data", HFILL }},
406 { &hf_mapi_encrypted_data,
407 { "Encrypted data", "mapi.encrypted_data", FT_BYTES, BASE_HEX,
408 NULL, 0, "Encrypted data", HFILL }},
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 }},
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 }},
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 }},
422 { &hf_mapi_decrypted_data,
423 { "Decrypted data", "mapi.decrypted.data", FT_BYTES, BASE_HEX,
424 NULL, 0x0, "Decrypted data", HFILL }},
427 { "Length", "mapi.pdu.len", FT_UINT16, BASE_DEC,
428 NULL, 0x0, "Size of the command PDU", HFILL }},
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 }},
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 }}
440 static gint *ett[] = {
442 &ett_mapi_decrypted_pdu
444 module_t *mapi_module;
446 proto_dcerpc_mapi = proto_register_protocol(
447 "Microsoft Exchange MAPI", "MAPI", "mapi");
449 proto_register_field_array(proto_dcerpc_mapi, 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",
455 "Whether the dissector should decrypt MAPI PDUs",
457 register_init_routine(mapi_decrypt_init);
461 proto_reg_handoff_dcerpc_mapi(void)
463 /* Register protocol as dcerpc */
465 dcerpc_init_uuid(proto_dcerpc_mapi, ett_dcerpc_mapi,
466 &uuid_dcerpc_mapi, ver_dcerpc_mapi,
467 dcerpc_mapi_dissectors, hf_mapi_opnum);