some compilers dont like unnamed unions and structs
[obnox/wireshark/wip.git] / epan / dissectors / packet-dcerpc-mapi.c
1 /* packet-dcerpc-mapi.c
2  * Routines for MS Exchange MAPI
3  * Copyright 2002, Ronnie Sahlberg
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
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 "packet-windows-common.h"      /* for "NT_errors[]" */
36 #include <epan/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_notification_port = -1;
53 static int hf_mapi_notification_payload = -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
76 static int
77 mapi_decrypt_pdu(tvbuff_t *tvb, int offset,
78         packet_info *pinfo, proto_tree *tree, guint8 *drep)
79 {
80         dcerpc_info *di;
81         guint8 *decrypted_data;
82         tvbuff_t *decrypted_tvb;
83         guint32 length;
84         gint len, reported_len;
85         const guint8 *ptr;
86         gint i;
87         guint16 pdu_len;
88         proto_item *it = NULL;
89         proto_tree *tr = NULL;
90
91         di=pinfo->private_data;
92         if(di->conformant_run){
93                 return offset;
94         }
95
96         offset=dissect_ndr_uint32(tvb, offset, pinfo, tree, drep, hf_mapi_decrypted_data_maxlen, NULL);
97         offset=dissect_ndr_uint32(tvb, offset, pinfo, tree, drep, hf_mapi_decrypted_data_offset, NULL);
98         offset=dissect_ndr_uint32(tvb, offset, pinfo, tree, drep, hf_mapi_decrypted_data_len, &length);
99
100         len = tvb_length_remaining(tvb, offset);
101         reported_len = tvb_reported_length_remaining(tvb, offset);
102         if((guint32)reported_len > length)
103                 reported_len = length;
104         if(len > reported_len)
105                 len = reported_len;
106
107         ptr=tvb_get_ptr(tvb, offset, len);
108         decrypted_data=g_malloc(len);
109         for(i=0;i<len;i++){
110                 /*
111                  * Now *that's* secure encryption!
112                  */
113                 decrypted_data[i]=ptr[i]^0xa5;
114         }
115
116         /* Allocate a new tvbuff, referring to the decrypted data. */
117         decrypted_tvb=tvb_new_real_data(decrypted_data, len, reported_len);
118
119         /* Arrange that the allocated packet data copy be freed when the
120            tvbuff is freed. */
121         tvb_set_free_cb(decrypted_tvb, g_free);
122
123         /* Add the tvbuff to the list of tvbuffs to which the tvbuff we
124            were handed refers, so it'll get cleaned up when that tvbuff
125            is cleaned up. */
126         tvb_set_child_real_data_tvbuff(tvb, decrypted_tvb);
127
128         /* Add the decrypted data to the data source list. */
129         add_new_data_source(pinfo, decrypted_tvb, "Decrypted MAPI");
130
131         /* decrypted PDU */
132         /* All from 10 minutes eyeballing. This may be wrong.
133            The PDU is NOT NDR encoded. So this completely new marshalling
134            used by MAPI needs to be figured out.
135
136            It seems that ASCII text strings always are NULL terminated,
137            also no obvious string-length-byte can be seen so it seems the
138            length of strings are determined by searching the terminating null
139            byte.
140
141            The first two bytes of the PDU is the length of the PDU including
142            the two length bytes.
143            The third byte may be a subcommand byte ?
144
145            After the PDU comes, in requests a 4 byte thing. Which is either
146            (not very often) 0xffffffff or something else. If it is
147            'something else' these four bytes are repeated for the matching
148            response packet.
149            In some repsonse packets, this 4 byte trailer are sometimes followed
150            by some other data. Unclear if this is just random padding or actual
151            data. Seems a bit non-random for padding though.
152
153            Some response packets have a PDU of 2 bytes only, ie only the
154            2 byte length field followed by the 4 byte trailer.
155            strange.
156            perhaps the 4 byte trailers, and the extra trailers have some
157            special meaning?
158            More work needs to be done in this area.
159         */
160         it=proto_tree_add_text(tree, decrypted_tvb, 0, len, "Decrypted MAPI PDU");
161         tr=proto_item_add_subtree(it, ett_mapi_decrypted_pdu);
162
163         pdu_len=tvb_get_letohs(decrypted_tvb, 0);
164         proto_tree_add_uint(tr, hf_mapi_pdu_len, decrypted_tvb, 0, 2, pdu_len);
165
166         /*XXX call dissector here */
167         proto_tree_add_item(tr, hf_mapi_decrypted_data, decrypted_tvb, 2, pdu_len-2, FALSE);
168
169         offset+=len;
170
171         return offset;
172 }
173
174 static int
175 mapi_logon_rqst(tvbuff_t *tvb, int offset,
176         packet_info *pinfo, proto_tree *tree, guint8 *drep)
177 {
178         offset = dissect_ndr_cvstring(tvb, offset, pinfo, tree, drep,
179                         sizeof(guint8), hf_mapi_unknown_string, TRUE, NULL);
180
181         DISSECT_UNKNOWN(tvb_length_remaining(tvb, offset));
182
183         return offset;
184 }
185
186 /* The strings in this function are decoded properly on seen captures.
187 There might be offsets/padding mismatched due to potential pointer expansions
188 or padding bytes. Captures where this code breaks will tell us about that */
189 static int
190 mapi_logon_reply(tvbuff_t *tvb, int offset,
191         packet_info *pinfo, proto_tree *tree, guint8 *drep)
192 {
193         offset = dissect_nt_policy_hnd(tvb, offset, pinfo, tree, drep,
194                                        hf_mapi_hnd, NULL, NULL, FALSE, FALSE);
195
196         DISSECT_UNKNOWN(20); /* this is 20 bytes, unless there are pointers */
197
198         offset = dissect_ndr_cvstring(tvb, offset, pinfo, tree, drep,
199                         sizeof(guint8), hf_mapi_unknown_string, TRUE, NULL);
200
201         /* Was DISSECT_UNKNOWN(6), but the 1 or 2 bytes the comment that
202            was here referred to probably were padding, if they were seen;
203            in another capture, there are 5 bytes there - it's probably a
204            4-byte quantity, always aligned on a 4-byte boundary. */
205         offset = dissect_ndr_uint32 (tvb, offset, pinfo, tree, drep,
206                         hf_mapi_unknown_long, NULL);
207
208         offset = dissect_ndr_cvstring(tvb, offset, pinfo, tree, drep,
209                         sizeof(guint8), hf_mapi_unknown_string, TRUE, NULL);
210
211         DISSECT_UNKNOWN( tvb_length_remaining(tvb, offset)-4 );
212
213         offset = dissect_ntstatus(tvb, offset, pinfo, tree, drep,
214                         hf_mapi_rc, NULL);
215
216         return offset;
217 }
218
219 static int
220 mapi_ec_do_rpc_request(tvbuff_t *tvb, int offset,
221         packet_info *pinfo, proto_tree *tree, guint8 *drep)
222 {
223         offset = dissect_nt_policy_hnd(tvb, offset, pinfo, tree, drep,
224                                        hf_mapi_hnd, NULL, NULL, FALSE, FALSE);
225
226         if(!mapi_decrypt){
227                 /* this is a unidimensional varying and conformant array of
228                    encrypted data */
229                 offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
230                                 dissect_ndr_byte_array, NDR_POINTER_REF,
231                                 "Encrypted data", hf_mapi_encrypted_data);
232         } else {
233                 offset = mapi_decrypt_pdu(tvb, offset, pinfo, tree, drep);
234         }
235
236         /* length of encrypted data. */
237         offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
238                         hf_mapi_encap_datalen, NULL);
239
240         offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
241                         hf_mapi_unknown_short, NULL);
242
243         return offset;
244 }
245 static int
246 mapi_ec_do_rpc_reply(tvbuff_t *tvb, int offset,
247         packet_info *pinfo, proto_tree *tree, guint8 *drep)
248 {
249         offset = dissect_nt_policy_hnd(tvb, offset, pinfo, tree, drep,
250                                        hf_mapi_hnd, NULL, NULL, FALSE, FALSE);
251
252         if(!mapi_decrypt){
253                 /* this is a unidimensional varying and conformant array of
254                    encrypted data */
255                 offset = dissect_ndr_pointer(tvb, offset, pinfo, tree, drep,
256                                 dissect_ndr_byte_array, NDR_POINTER_REF,
257                                 "Encrypted data", hf_mapi_encrypted_data);
258         } else {
259                 offset = mapi_decrypt_pdu(tvb, offset, pinfo, tree, drep);
260         }
261
262         /* length of encrypted data */
263         offset = dissect_ndr_uint16 (tvb, offset, pinfo, tree, drep,
264                         hf_mapi_encap_datalen, NULL);
265
266         offset = dissect_ntstatus(tvb, offset, pinfo, tree, drep,
267                         hf_mapi_rc, NULL);
268
269         return offset;
270 }
271
272 static int
273 mapi_logoff_rqst(tvbuff_t *tvb, int offset,
274         packet_info *pinfo, proto_tree *tree, guint8 *drep)
275 {
276         offset = dissect_nt_policy_hnd(tvb, offset, pinfo, tree, drep,
277                                        hf_mapi_hnd, NULL, NULL, FALSE, FALSE);
278
279         return offset;
280 }
281
282 static int
283 mapi_logoff_reply(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         offset = dissect_ntstatus(tvb, offset, pinfo, tree, drep,
290                         hf_mapi_rc, NULL);
291
292         return offset;
293 }
294
295 static int
296 mapi_reg_push_notification_rqst(tvbuff_t *tvb, int offset,
297         packet_info *pinfo _U_, proto_tree *tree, guint8 *drep _U_)
298 {
299         static dissector_handle_t newmail_handle;
300
301         proto_tree_add_item(tree, hf_mapi_notification_payload, tvb, 28, 8,
302                             FALSE);
303         proto_tree_add_item(tree, hf_mapi_notification_port, tvb, 50, 2,
304                             FALSE);
305
306         /* Register the notification port from this packet with the newmail
307            dissector. */
308         newmail_handle = find_dissector("newmail");
309         dissector_add("udp.port", tvb_get_ntohs(tvb, 50), newmail_handle);
310
311         offset = 52;
312         return offset;
313 }
314
315 static dcerpc_sub_dissector dcerpc_mapi_dissectors[] = {
316         { MAPI_EC_DO_CONNECT,   "EcDoConnect",
317                 mapi_logon_rqst,
318                 mapi_logon_reply },
319         { MAPI_EC_DO_DISCONNECT,"EcDoDisconnect",
320                 mapi_logoff_rqst,
321                 mapi_logoff_reply },
322         { MAPI_EC_DO_RPC,       "EcDoRpc",
323                 mapi_ec_do_rpc_request,
324                 mapi_ec_do_rpc_reply },
325         { MAPI_EC_GET_MORE_RPC, "EcGetMoreRpc", NULL, NULL },
326         { MAPI_EC_REGISTER_PUSH_NOTIFICATION, "EcRRegisterPushNotification",
327                 mapi_reg_push_notification_rqst, NULL },
328         { MAPI_EC_UNREGISTER_PUSH_NOTIFICATION, "EcRUnregisterPushNotification",
329                 NULL, NULL }, /* Not used by Microsoft Outlook */
330         { MAPI_EC_DUMMY_RPC, "EcDummyRpc", NULL, NULL },
331         { MAPI_EC_GET_DC_NAME, "EcRGetDCName", NULL, NULL },
332         { MAPI_EC_NET_GET_DC_NAME, "EcRNetGetDCName", NULL, NULL },
333         { MAPI_EC_DO_RPC_EXT, "EcDoRpcExt", NULL, NULL },
334         {0, NULL, NULL,  NULL }
335 };
336
337 void
338 proto_register_dcerpc_mapi(void)
339 {
340
341 static hf_register_info hf[] = {
342         { &hf_mapi_opnum,
343                 { "Operation", "mapi.opnum", FT_UINT16, BASE_DEC,
344                   NULL, 0x0, "", HFILL }},
345
346         { &hf_mapi_hnd,
347                 { "Context Handle", "mapi.hnd", FT_BYTES, BASE_NONE,
348                 NULL, 0x0, "", HFILL }},
349
350         { &hf_mapi_rc,
351                 { "Return code", "mapi.rc", FT_UINT32, BASE_HEX,
352                 VALS (NT_errors), 0x0, "", HFILL }},
353
354         { &hf_mapi_unknown_string,
355                 { "Unknown string", "mapi.unknown_string", FT_STRING, BASE_NONE,
356                 NULL, 0, "Unknown string. If you know what this is, contact wireshark developers.", HFILL }},
357
358         { &hf_mapi_unknown_short,
359                 { "Unknown short", "mapi.unknown_short", FT_UINT16, BASE_HEX,
360                 NULL, 0, "Unknown short. If you know what this is, contact wireshark developers.", HFILL }},
361
362         { &hf_mapi_unknown_long,
363                 { "Unknown long", "mapi.unknown_long", FT_UINT32, BASE_HEX,
364                 NULL, 0, "Unknown long. If you know what this is, contact wireshark developers.", HFILL }},
365
366         { &hf_mapi_encap_datalen,
367                 { "Length", "mapi.encap_len", FT_UINT16, BASE_DEC,
368                 NULL, 0x0, "Length of encapsulated/encrypted data", HFILL }},
369
370         { &hf_mapi_encrypted_data,
371                 { "Encrypted data", "mapi.encrypted_data", FT_BYTES, BASE_HEX,
372                 NULL, 0, "Encrypted data", HFILL }},
373
374         { &hf_mapi_decrypted_data_maxlen,
375                 { "Max Length", "mapi.decrypted.data.maxlen", FT_UINT32, BASE_DEC,
376                 NULL, 0x0, "Maximum size of buffer for decrypted data", HFILL }},
377
378         { &hf_mapi_decrypted_data_offset,
379                 { "Offset", "mapi.decrypted.data.offset", FT_UINT32, BASE_DEC,
380                 NULL, 0x0, "Offset into buffer for decrypted data", HFILL }},
381
382         { &hf_mapi_decrypted_data_len,
383                 { "Length", "mapi.decrypted.data.len", FT_UINT32, BASE_DEC,
384                 NULL, 0x0, "Used size of buffer for decrypted data", HFILL }},
385
386         { &hf_mapi_decrypted_data,
387                 { "Decrypted data", "mapi.decrypted.data", FT_BYTES, BASE_HEX,
388                 NULL, 0x0, "Decrypted data", HFILL }},
389
390         { &hf_mapi_pdu_len,
391                 { "Length", "mapi.pdu.len", FT_UINT16, BASE_DEC,
392                 NULL, 0x0, "Size of the command PDU", HFILL }},
393
394         { &hf_mapi_notification_port,
395                 { "Notification port", "mapi.notification_port", FT_UINT16,
396                   BASE_DEC, NULL, 0x0,
397                   "UDP port which newmail protocol notifications are sent to on the client", HFILL }},
398
399         { &hf_mapi_notification_payload,
400                 { "Notification payload", "mapi.notification_payload",
401                   FT_BYTES, BASE_NONE, NULL, 0x0,
402                   "Payload to be sent in newmail protocol notification packets", HFILL }},
403
404         };
405
406
407         static gint *ett[] = {
408                 &ett_dcerpc_mapi,
409                 &ett_mapi_decrypted_pdu
410         };
411         module_t *mapi_module;
412
413         proto_dcerpc_mapi = proto_register_protocol(
414                 "Microsoft Exchange MAPI", "MAPI", "mapi");
415
416         proto_register_field_array(proto_dcerpc_mapi, hf,
417                                    array_length(hf));
418         proto_register_subtree_array(ett, array_length(ett));
419         mapi_module = prefs_register_protocol(proto_dcerpc_mapi, NULL);
420         prefs_register_bool_preference(mapi_module, "decrypt",
421                 "Decrypt MAPI PDUs",
422                 "Whether the dissector should decrypt MAPI PDUs",
423                 &mapi_decrypt);
424 }
425
426 void
427 proto_reg_handoff_dcerpc_mapi(void)
428 {
429         /* Register protocol as dcerpc */
430
431         dcerpc_init_uuid(proto_dcerpc_mapi, ett_dcerpc_mapi,
432                          &uuid_dcerpc_mapi, ver_dcerpc_mapi,
433                          dcerpc_mapi_dissectors, hf_mapi_opnum);
434 }