Move sourcelists into Makefile.common
[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.26 2004/01/19 20:10:33 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_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, guint8 *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, guint8 *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, guint8 *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_ec_do_rpc_request(tvbuff_t *tvb, int offset,
284         packet_info *pinfo, proto_tree *tree, guint8 *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_ec_do_rpc_reply(tvbuff_t *tvb, int offset,
310         packet_info *pinfo, proto_tree *tree, guint8 *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, guint8 *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, guint8 *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_EC_DO_CONNECT,   "EcDoConnect",
361                 mapi_logon_rqst,
362                 mapi_logon_reply },
363         { MAPI_EC_DO_DISCONNECT,"EcDoDisconnect",
364                 mapi_logoff_rqst,
365                 mapi_logoff_reply },
366         { MAPI_EC_DO_RPC,       "EcDoRpc",
367                 mapi_ec_do_rpc_request,
368                 mapi_ec_do_rpc_reply },
369         { MAPI_EC_GET_MORE_RPC, "EcGetMoreRpc", NULL, NULL },
370         { MAPI_EC_REGISTER_PUSH_NOTIFICATION, "EcRRegisterPushNotification",
371                 NULL, NULL },
372         { MAPI_EC_UNREGISTER_PUSH_NOTIFICATION, "EcRUnregisterPushNotification",
373                 NULL, NULL },
374         { MAPI_EC_DUMMY_RPC, "EcDummyRpc", NULL, NULL },
375         { MAPI_EC_GET_DC_NAME, "EcRGetDCName", NULL, NULL },
376         { MAPI_EC_NET_GET_DC_NAME, "EcRNetGetDCName", NULL, NULL },
377         { MAPI_EC_DO_RPC_EXT, "EcDoRpcExt", NULL, NULL },
378         {0, NULL, NULL,  NULL }
379 };
380
381 void
382 proto_register_dcerpc_mapi(void)
383 {
384
385 static hf_register_info hf[] = {
386         { &hf_mapi_opnum,
387                 { "Operation", "mapi.opnum", FT_UINT16, BASE_DEC,
388                   NULL, 0x0, "", HFILL }},
389
390         { &hf_mapi_hnd,
391                 { "Context Handle", "mapi.hnd", FT_BYTES, BASE_NONE,
392                 NULL, 0x0, "", HFILL }},
393
394         { &hf_mapi_rc,
395                 { "Return code", "mapi.rc", FT_UINT32, BASE_HEX,
396                 VALS (NT_errors), 0x0, "", HFILL }},
397
398         { &hf_mapi_unknown_string,
399                 { "Unknown string", "mapi.unknown_string", FT_STRING, BASE_NONE,
400                 NULL, 0, "Unknown string. If you know what this is, contact ethereal developers.", HFILL }},
401
402         { &hf_mapi_unknown_short,
403                 { "Unknown short", "mapi.unknown_short", FT_UINT16, BASE_HEX,
404                 NULL, 0, "Unknown short. If you know what this is, contact ethereal developers.", HFILL }},
405
406         { &hf_mapi_unknown_long,
407                 { "Unknown long", "mapi.unknown_long", FT_UINT32, BASE_HEX,
408                 NULL, 0, "Unknown long. If you know what this is, contact ethereal developers.", HFILL }},
409
410         { &hf_mapi_encap_datalen,
411                 { "Length", "mapi.encap_len", FT_UINT16, BASE_DEC,
412                 NULL, 0x0, "Length of encapsulated/encrypted data", HFILL }},
413
414         { &hf_mapi_encrypted_data,
415                 { "Encrypted data", "mapi.encrypted_data", FT_BYTES, BASE_HEX,
416                 NULL, 0, "Encrypted data", HFILL }},
417
418         { &hf_mapi_decrypted_data_maxlen,
419                 { "Max Length", "mapi.decrypted.data.maxlen", FT_UINT32, BASE_DEC,
420                 NULL, 0x0, "Maximum size of buffer for decrypted data", HFILL }},
421
422         { &hf_mapi_decrypted_data_offset,
423                 { "Offset", "mapi.decrypted.data.offset", FT_UINT32, BASE_DEC,
424                 NULL, 0x0, "Offset into buffer for decrypted data", HFILL }},
425
426         { &hf_mapi_decrypted_data_len,
427                 { "Length", "mapi.decrypted.data.len", FT_UINT32, BASE_DEC,
428                 NULL, 0x0, "Used size of buffer for decrypted data", HFILL }},
429
430         { &hf_mapi_decrypted_data,
431                 { "Decrypted data", "mapi.decrypted.data", FT_BYTES, BASE_HEX,
432                 NULL, 0x0, "Decrypted data", HFILL }},
433
434         { &hf_mapi_pdu_len,
435                 { "Length", "mapi.pdu.len", FT_UINT16, BASE_DEC,
436                 NULL, 0x0, "Size of the command PDU", HFILL }},
437
438         { &hf_mapi_pdu_trailer,
439                 { "Trailer", "mapi.pdu.trailer", FT_UINT32, BASE_HEX,
440                 NULL, 0x0, "If you know what this is, contact ethereal developers", HFILL }},
441
442         { &hf_mapi_pdu_extra_trailer,
443                 { "unknown", "mapi.pdu.extra_trailer", FT_BYTES, BASE_HEX,
444                 NULL, 0x0, "If you know what this is, contact ethereal developers", HFILL }}
445         };
446
447
448         static gint *ett[] = {
449                 &ett_dcerpc_mapi,
450                 &ett_mapi_decrypted_pdu
451         };
452         module_t *mapi_module;
453
454         proto_dcerpc_mapi = proto_register_protocol(
455                 "Microsoft Exchange MAPI", "MAPI", "mapi");
456
457         proto_register_field_array(proto_dcerpc_mapi, hf,
458                                    array_length(hf));
459         proto_register_subtree_array(ett, array_length(ett));
460         mapi_module = prefs_register_protocol(proto_dcerpc_mapi, NULL);
461         prefs_register_bool_preference(mapi_module, "decrypt",
462                 "Decrypt MAPI PDUs",
463                 "Whether the dissector should decrypt MAPI PDUs",
464                 &mapi_decrypt);
465         register_init_routine(mapi_decrypt_init);
466 }
467
468 void
469 proto_reg_handoff_dcerpc_mapi(void)
470 {
471         /* Register protocol as dcerpc */
472
473         dcerpc_init_uuid(proto_dcerpc_mapi, ett_dcerpc_mapi,
474                          &uuid_dcerpc_mapi, ver_dcerpc_mapi,
475                          dcerpc_mapi_dissectors, hf_mapi_opnum);
476 }