diff options
Diffstat (limited to 'epan/dissectors/packet-dcerpc.c')
-rw-r--r-- | epan/dissectors/packet-dcerpc.c | 7265 |
1 files changed, 7265 insertions, 0 deletions
diff --git a/epan/dissectors/packet-dcerpc.c b/epan/dissectors/packet-dcerpc.c new file mode 100644 index 00000000..8d31002d --- /dev/null +++ b/epan/dissectors/packet-dcerpc.c @@ -0,0 +1,7265 @@ +/* packet-dcerpc.c + * Routines for DCERPC packet disassembly + * Copyright 2001, Todd Sabin <tas[AT]webspan.net> + * Copyright 2003, Tim Potter <tpot[AT]samba.org> + * Copyright 2010, Julien Kerihuel <j.kerihuel[AT]openchange.org> + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/* The DCE RPC 1.1 specification can be found at: + * + * https://publications.opengroup.org/c706 + * https://pubs.opengroup.org/onlinepubs/009629399/ + * https://pubs.opengroup.org/onlinepubs/009629399/toc.htm + * https://pubs.opengroup.org/onlinepubs/009629399/toc.pdf + * + * Microsoft extensions can be found at: + * + * MS-WPO section 7.3.1 "RPC": + * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wpo/7d2df784-557e-4fde-9281-9509653a0f17 + */ + +#include "config.h" + +#include <stdio.h> /* for sscanf() */ +#include <epan/packet.h> +#include <epan/exceptions.h> +#include <epan/prefs.h> +#include <epan/reassemble.h> +#include <epan/tap.h> +#include <epan/srt_table.h> +#include <epan/expert.h> +#include <epan/addr_resolv.h> +#include <epan/show_exception.h> +#include <epan/decode_as.h> +#include <epan/proto_data.h> + +#include <wsutil/str_util.h> +#include "packet-tcp.h" +#include "packet-dcerpc.h" +#include "packet-dcerpc-nt.h" + +void proto_register_dcerpc(void); +void proto_reg_handoff_dcerpc(void); + +static dissector_handle_t dcerpc_tcp_handle; + +static int dcerpc_tap = -1; + +/* 32bit Network Data Representation, see DCE/RPC Appendix I */ +static e_guid_t uuid_data_repr_proto = { 0x8a885d04, 0x1ceb, 0x11c9, + { 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60 } }; + +/* 64bit Network Data Representation, introduced in Windows Server 2008 */ +static e_guid_t uuid_ndr64 = { 0x71710533, 0xbeba, 0x4937, + { 0x83, 0x19, 0xb5, 0xdb, 0xef, 0x9c, 0xcc, 0x36 } }; + +/* see [MS-OXRPC] Appendix A: Full IDL, https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxcrpc/70adbb71-85a1-4023-bfdb-41e32ff37bf1 */ +static e_guid_t uuid_asyncemsmdb = { 0x5261574a, 0x4572, 0x206e, + { 0xb2, 0x68, 0x6b, 0x19, 0x92, 0x13, 0xb4, 0xe4 } }; + +static const value_string pckt_vals[] = { + { PDU_REQ, "Request"}, + { PDU_PING, "Ping"}, + { PDU_RESP, "Response"}, + { PDU_FAULT, "Fault"}, + { PDU_WORKING, "Working"}, + { PDU_NOCALL, "Nocall"}, + { PDU_REJECT, "Reject"}, + { PDU_ACK, "Ack"}, + { PDU_CL_CANCEL, "Cl_cancel"}, + { PDU_FACK, "Fack"}, + { PDU_CANCEL_ACK, "Cancel_ack"}, + { PDU_BIND, "Bind"}, + { PDU_BIND_ACK, "Bind_ack"}, + { PDU_BIND_NAK, "Bind_nak"}, + { PDU_ALTER, "Alter_context"}, + { PDU_ALTER_ACK, "Alter_context_resp"}, + { PDU_AUTH3, "AUTH3"}, + { PDU_SHUTDOWN, "Shutdown"}, + { PDU_CO_CANCEL, "Co_cancel"}, + { PDU_ORPHANED, "Orphaned"}, + { PDU_RTS, "RPC-over-HTTP RTS"}, + { 0, NULL } +}; + +static const value_string drep_byteorder_vals[] = { + { 0, "Big-endian" }, + { 1, "Little-endian" }, + { 0, NULL } +}; + +static const value_string drep_character_vals[] = { + { 0, "ASCII" }, + { 1, "EBCDIC" }, + { 0, NULL } +}; + +#define DCE_RPC_DREP_FP_IEEE 0 +#define DCE_RPC_DREP_FP_VAX 1 +#define DCE_RPC_DREP_FP_CRAY 2 +#define DCE_RPC_DREP_FP_IBM 3 + +static const value_string drep_fp_vals[] = { + { DCE_RPC_DREP_FP_IEEE, "IEEE" }, + { DCE_RPC_DREP_FP_VAX, "VAX" }, + { DCE_RPC_DREP_FP_CRAY, "Cray" }, + { DCE_RPC_DREP_FP_IBM, "IBM" }, + { 0, NULL } +}; + +/* + * Authentication services. + */ +static const value_string authn_protocol_vals[] = { + { DCE_C_RPC_AUTHN_PROTOCOL_NONE, "None" }, + { DCE_C_RPC_AUTHN_PROTOCOL_KRB5, "Kerberos 5" }, + { DCE_C_RPC_AUTHN_PROTOCOL_SPNEGO, "SPNEGO" }, + { DCE_C_RPC_AUTHN_PROTOCOL_NTLMSSP, "NTLMSSP" }, + { DCE_C_RPC_AUTHN_PROTOCOL_GSS_SCHANNEL, "SCHANNEL SSP" }, + { DCE_C_RPC_AUTHN_PROTOCOL_GSS_KERBEROS, "Kerberos SSP" }, + { DCE_C_RPC_AUTHN_PROTOCOL_DPA, + "Distributed Password Authentication SSP"}, + { DCE_C_RPC_AUTHN_PROTOCOL_MSN, "MSN SSP"}, + { DCE_C_RPC_AUTHN_PROTOCOL_DIGEST, "Digest SSP"}, + { DCE_C_RPC_AUTHN_PROTOCOL_SEC_CHAN, "NETLOGON Secure Channel" }, + { DCE_C_RPC_AUTHN_PROTOCOL_MQ, "MSMQ SSP"}, + { 0, NULL } +}; + +/* + * Protection levels. + */ +static const value_string authn_level_vals[] = { + { DCE_C_AUTHN_LEVEL_NONE, "None" }, + { DCE_C_AUTHN_LEVEL_CONNECT, "Connect" }, + { DCE_C_AUTHN_LEVEL_CALL, "Call" }, + { DCE_C_AUTHN_LEVEL_PKT, "Packet" }, + { DCE_C_AUTHN_LEVEL_PKT_INTEGRITY, "Packet integrity" }, + { DCE_C_AUTHN_LEVEL_PKT_PRIVACY, "Packet privacy" }, + { 0, NULL } +}; + +/* + * Flag bits in first flag field in connectionless PDU header. + */ +#define PFCL1_RESERVED_01 0x01 /* Reserved for use by implementations */ +#define PFCL1_LASTFRAG 0x02 /* If set, the PDU is the last + * fragment of a multi-PDU + * transmission */ +#define PFCL1_FRAG 0x04 /* If set, the PDU is a fragment of + a multi-PDU transmission */ +#define PFCL1_NOFACK 0x08 /* If set, the receiver is not + * requested to send a `fack' PDU + * for the fragment */ +#define PFCL1_MAYBE 0x10 /* If set, the PDU is for a `maybe' + * request */ +#define PFCL1_IDEMPOTENT 0x20 /* If set, the PDU is for an idempotent + * request */ +#define PFCL1_BROADCAST 0x40 /* If set, the PDU is for a broadcast + * request */ +#define PFCL1_RESERVED_80 0x80 /* Reserved for use by implementations */ + +/* + * Flag bits in second flag field in connectionless PDU header. + */ +#define PFCL2_RESERVED_01 0x01 /* Reserved for use by implementations */ +#define PFCL2_CANCEL_PENDING 0x02 /* Cancel pending at the call end */ +#define PFCL2_RESERVED_04 0x04 /* Reserved for future use */ +#define PFCL2_RESERVED_08 0x08 /* Reserved for future use */ +#define PFCL2_RESERVED_10 0x10 /* Reserved for future use */ +#define PFCL2_RESERVED_20 0x20 /* Reserved for future use */ +#define PFCL2_RESERVED_40 0x40 /* Reserved for future use */ +#define PFCL2_RESERVED_80 0x80 /* Reserved for future use */ + +/* + * Flag bits in connection-oriented PDU header. + */ +#define PFC_FIRST_FRAG 0x01 /* First fragment */ +#define PFC_LAST_FRAG 0x02 /* Last fragment */ +#define PFC_PENDING_CANCEL 0x04 /* Cancel was pending at sender */ +#define PFC_HDR_SIGNING PFC_PENDING_CANCEL /* on bind and alter req */ +#define PFC_RESERVED_1 0x08 +#define PFC_CONC_MPX 0x10 /* supports concurrent multiplexing + * of a single connection. */ +#define PFC_DID_NOT_EXECUTE 0x20 /* only meaningful on `fault' packet; + * if true, guaranteed call did not + * execute. */ +#define PFC_MAYBE 0x40 /* `maybe' call semantics requested */ +#define PFC_OBJECT_UUID 0x80 /* if true, a non-nil object UUID + * was specified in the handle, and + * is present in the optional object + * field. If false, the object field + * is omitted. */ + +/* + * Tests whether a connection-oriented PDU is fragmented; returns TRUE if + * it's not fragmented (i.e., this is both the first *and* last fragment), + * and FALSE otherwise. + */ +#define PFC_NOT_FRAGMENTED(hdr) \ + ((hdr->flags&(PFC_FIRST_FRAG|PFC_LAST_FRAG)) == (PFC_FIRST_FRAG|PFC_LAST_FRAG)) + +/* + * Presentation context negotiation result. + */ +static const value_string p_cont_result_vals[] = { + { 0, "Acceptance" }, + { 1, "User rejection" }, + { 2, "Provider rejection" }, + { 3, "Negotiate ACK" }, /* [MS-RPCE] 2.2.2.4 */ + { 0, NULL } +}; + +/* + * Presentation context negotiation rejection reasons. + */ +static const value_string p_provider_reason_vals[] = { + { 0, "Reason not specified" }, + { 1, "Abstract syntax not supported" }, + { 2, "Proposed transfer syntaxes not supported" }, + { 3, "Local limit exceeded" }, + { 0, NULL } +}; + +/* + * Reject reasons. + */ +#define REASON_NOT_SPECIFIED 0 +#define TEMPORARY_CONGESTION 1 +#define LOCAL_LIMIT_EXCEEDED 2 +#define CALLED_PADDR_UNKNOWN 3 /* not used */ +#define PROTOCOL_VERSION_NOT_SUPPORTED 4 +#define DEFAULT_CONTEXT_NOT_SUPPORTED 5 /* not used */ +#define USER_DATA_NOT_READABLE 6 /* not used */ +#define NO_PSAP_AVAILABLE 7 /* not used */ +#define AUTH_TYPE_NOT_RECOGNIZED 8 /* [MS-RPCE] 2.2.2.5 */ +#define INVALID_CHECKSUM 9 /* [MS-RPCE] 2.2.2.5 */ + +static const value_string reject_reason_vals[] = { + { REASON_NOT_SPECIFIED, "Reason not specified" }, + { TEMPORARY_CONGESTION, "Temporary congestion" }, + { LOCAL_LIMIT_EXCEEDED, "Local limit exceeded" }, + { CALLED_PADDR_UNKNOWN, "Called paddr unknown" }, + { PROTOCOL_VERSION_NOT_SUPPORTED, "Protocol version not supported" }, + { DEFAULT_CONTEXT_NOT_SUPPORTED, "Default context not supported" }, + { USER_DATA_NOT_READABLE, "User data not readable" }, + { NO_PSAP_AVAILABLE, "No PSAP available" }, + { AUTH_TYPE_NOT_RECOGNIZED, "Authentication type not recognized" }, + { INVALID_CHECKSUM, "Invalid checksum" }, + { 0, NULL } +}; + +/* + * Reject status codes. + */ +static const value_string reject_status_vals[] = { + { 0, "Stub-defined exception" }, + { 0x00000001, "nca_s_fault_other" }, + { 0x00000005, "nca_s_fault_access_denied" }, + { 0x000006f7, "nca_s_fault_ndr" }, + { 0x000006d8, "nca_s_fault_cant_perform" }, + { 0x00000721, "nca_s_fault_sec_pkg_error" }, + { 0x1c000001, "nca_s_fault_int_div_by_zero" }, + { 0x1c000002, "nca_s_fault_addr_error" }, + { 0x1c000003, "nca_s_fault_fp_div_zero" }, + { 0x1c000004, "nca_s_fault_fp_underflow" }, + { 0x1c000005, "nca_s_fault_fp_overflow" }, + { 0x1c000006, "nca_s_fault_invalid_tag" }, + { 0x1c000007, "nca_s_fault_invalid_bound" }, + { 0x1c000008, "nca_rpc_version_mismatch" }, + { 0x1c000009, "nca_unspec_reject" }, + { 0x1c00000a, "nca_s_bad_actid" }, + { 0x1c00000b, "nca_who_are_you_failed" }, + { 0x1c00000c, "nca_manager_not_entered" }, + { 0x1c00000d, "nca_s_fault_cancel" }, + { 0x1c00000e, "nca_s_fault_ill_inst" }, + { 0x1c00000f, "nca_s_fault_fp_error" }, + { 0x1c000010, "nca_s_fault_int_overflow" }, + { 0x1c000014, "nca_s_fault_pipe_empty" }, + { 0x1c000015, "nca_s_fault_pipe_closed" }, + { 0x1c000016, "nca_s_fault_pipe_order" }, + { 0x1c000017, "nca_s_fault_pipe_discipline" }, + { 0x1c000018, "nca_s_fault_pipe_comm_error" }, + { 0x1c000019, "nca_s_fault_pipe_memory" }, + { 0x1c00001a, "nca_s_fault_context_mismatch" }, + { 0x1c00001b, "nca_s_fault_remote_no_memory" }, + { 0x1c00001c, "nca_invalid_pres_context_id" }, + { 0x1c00001d, "nca_unsupported_authn_level" }, + { 0x1c00001f, "nca_invalid_checksum" }, + { 0x1c000020, "nca_invalid_crc" }, + { 0x1c000021, "ncs_s_fault_user_defined" }, + { 0x1c000022, "nca_s_fault_tx_open_failed" }, + { 0x1c000023, "nca_s_fault_codeset_conv_error" }, + { 0x1c000024, "nca_s_fault_object_not_found" }, + { 0x1c000025, "nca_s_fault_no_client_stub" }, + { 0x1c010002, "nca_op_rng_error" }, + { 0x1c010003, "nca_unk_if"}, + { 0x1c010006, "nca_wrong_boot_time" }, + { 0x1c010009, "nca_s_you_crashed" }, + { 0x1c01000b, "nca_proto_error" }, + { 0x1c010013, "nca_out_args_too_big" }, + { 0x1c010014, "nca_server_too_busy" }, + { 0x1c010017, "nca_unsupported_type" }, + /* MS Windows specific values + * see: https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--1700-3999- + * and: https://docs.microsoft.com/en-us/windows/win32/seccrypto/common-hresult-values + * and: https://web.archive.org/web/20150825015741/http://www.megos.ch/support/doserrors.txt + * + * XXX - we might need a way to dynamically add entries here, as higher layer protocols use these values too, + * at least MS protocols (like DCOM) do it that way ... */ + { 0x80004001, "E_NOTIMPL" }, + { 0x80004003, "E_POINTER" }, + { 0x80004004, "E_ABORT" }, + { 0x8000FFFF, "E_UNEXPECTED" }, + { 0x80010105, "RPC_E_SERVERFAULT" }, + { 0x80010108, "RPC_E_DISCONNECTED" }, + { 0x80010113, "RPC_E_INVALID_IPID" }, + { 0x8001011F, "RPC_E_TIMEOUT" }, + { 0x80020003, "DISP_E_MEMBERNOTFOUND" }, + { 0x80020006, "DISP_E_UNKNOWNNAME" }, + { 0x8002000E, "DISP_E_BADPARAMCOUNT" }, + { 0x8004CB00, "CBA_E_MALFORMED" }, + { 0x8004CB01, "CBA_E_UNKNOWNOBJECT" }, + { 0x8004CB05, "CBA_E_INVALIDID" }, + { 0x8004CB09, "CBA_E_INVALIDCOOKIE" }, + { 0x8004CB0B, "CBA_E_QOSTYPEUNSUPPORTED" }, + { 0x8004CB0C, "CBA_E_QOSVALUEUNSUPPORTED" }, + { 0x8004CB0F, "CBA_E_NOTAPPLICABLE" }, + { 0x8004CB12, "CBA_E_LIMITVIOLATION" }, + { 0x8004CB13, "CBA_E_QOSTYPENOTAPPLICABLE" }, + { 0x8004CB18, "CBA_E_OUTOFPARTNERACCOS" }, + { 0x8004CB1C, "CBA_E_FLAGUNSUPPORTED" }, + { 0x8004CB23, "CBA_E_FRAMECOUNTUNSUPPORTED" }, + { 0x8004CB25, "CBA_E_MODECHANGE" }, + { 0x8007000E, "E_OUTOFMEMORY" }, + { 0x80070057, "E_INVALIDARG" }, + { 0x800706d1, "RPC_S_PROCNUM_OUT_OF_RANGE" }, + { 0x80070776, "OR_INVALID_OXID" }, + { 0, NULL } +}; + + +/* + * RTS Flags + */ +#define RTS_FLAG_NONE 0x0000 +#define RTS_FLAG_PING 0x0001 +#define RTS_FLAG_OTHER_CMD 0x0002 +#define RTS_FLAG_RECYCLE_CHANNEL 0x0004 +#define RTS_FLAG_IN_CHANNEL 0x0008 +#define RTS_FLAG_OUT_CHANNEL 0x0010 +#define RTS_FLAG_EOF 0x0020 +#define RTS_FLAG_ECHO 0x0040 + +/* + * RTS Commands + */ + +#define RTS_CMD_RECEIVEWINDOWSIZE 0x0 +#define RTS_CMD_FLOWCONTROLACK 0x1 +#define RTS_CMD_CONNECTIONTIMEOUT 0x2 +#define RTS_CMD_COOKIE 0x3 +#define RTS_CMD_CHANNELLIFETIME 0x4 +#define RTS_CMD_CLIENTKEEPALIVE 0x5 +#define RTS_CMD_VERSION 0x6 +#define RTS_CMD_EMPTY 0x7 +#define RTS_CMD_PADDING 0x8 +#define RTS_CMD_NEGATIVEANCE 0x9 +#define RTS_CMD_ANCE 0xA +#define RTS_CMD_CLIENTADDRESS 0xB +#define RTS_CMD_ASSOCIATIONGROUPID 0xC +#define RTS_CMD_DESTINATION 0xD +#define RTS_CMD_PINGTRAFFICSENTNOTIFY 0xE + +static const value_string rts_command_vals[] = { + { RTS_CMD_RECEIVEWINDOWSIZE, "ReceiveWindowSize" }, + { RTS_CMD_FLOWCONTROLACK, "FlowControlAck" }, + { RTS_CMD_CONNECTIONTIMEOUT, "ConnectionTimeOut" }, + { RTS_CMD_COOKIE, "Cookie" }, + { RTS_CMD_CHANNELLIFETIME, "ChannelLifetime" }, + { RTS_CMD_CLIENTKEEPALIVE, "ClientKeepalive" }, + { RTS_CMD_VERSION, "Version" }, + { RTS_CMD_EMPTY, "Empty" }, + { RTS_CMD_PADDING, "Padding" }, + { RTS_CMD_NEGATIVEANCE, "NegativeANCE" }, + { RTS_CMD_ANCE, "ANCE" }, + { RTS_CMD_CLIENTADDRESS, "ClientAddress" }, + { RTS_CMD_ASSOCIATIONGROUPID, "AssociationGroupId" }, + { RTS_CMD_DESTINATION, "Destination" }, + { RTS_CMD_PINGTRAFFICSENTNOTIFY, "PingTrafficSentNotify" }, + { 0x0, NULL } +}; + +/* + * RTS client address type + */ +#define RTS_IPV4 0 +#define RTS_IPV6 1 + +static const value_string rts_addresstype_vals[] = { + { RTS_IPV4, "IPV4" }, + { RTS_IPV6, "IPV6" }, + { 0x0, NULL } +}; + +/* + * RTS Forward destination + */ + +static const value_string rts_forward_destination_vals[] = { + { 0x0, "FDClient" }, + { 0x1, "FDInProxy" }, + { 0x2, "FDServer" }, + { 0x3, "FDOutProxy" }, + { 0x0, NULL } +}; + +/* we need to keep track of what transport were used, ie what handle we came + * in through so we know what kind of pinfo->dce_smb_fid was passed to us. + */ +/* Value of -1 is reserved for "not DCE packet" in packet_info.dcetransporttype. */ +#define DCE_TRANSPORT_UNKNOWN 0 +#define DCE_CN_TRANSPORT_SMBPIPE 1 + + +static int proto_dcerpc = -1; + +/* field defines */ +static int hf_dcerpc_request_in = -1; +static int hf_dcerpc_time = -1; +static int hf_dcerpc_response_in = -1; +static int hf_dcerpc_ver = -1; +static int hf_dcerpc_ver_minor = -1; +static int hf_dcerpc_packet_type = -1; +static int hf_dcerpc_cn_flags = -1; +static int hf_dcerpc_cn_flags_first_frag = -1; +static int hf_dcerpc_cn_flags_last_frag = -1; +static int hf_dcerpc_cn_flags_cancel_pending = -1; +static int hf_dcerpc_cn_flags_reserved = -1; +static int hf_dcerpc_cn_flags_mpx = -1; +static int hf_dcerpc_cn_flags_dne = -1; +static int hf_dcerpc_cn_flags_maybe = -1; +static int hf_dcerpc_cn_flags_object = -1; +static int hf_dcerpc_drep = -1; + int hf_dcerpc_drep_byteorder = -1; + int hf_dcerpc_ndr_padding = -1; +static int hf_dcerpc_drep_character = -1; +static int hf_dcerpc_drep_fp = -1; +static int hf_dcerpc_cn_frag_len = -1; +static int hf_dcerpc_cn_auth_len = -1; +static int hf_dcerpc_cn_call_id = -1; +static int hf_dcerpc_cn_max_xmit = -1; +static int hf_dcerpc_cn_max_recv = -1; +static int hf_dcerpc_cn_assoc_group = -1; +static int hf_dcerpc_cn_num_ctx_items = -1; +static int hf_dcerpc_cn_ctx_item = -1; +static int hf_dcerpc_cn_ctx_id = -1; +static int hf_dcerpc_cn_num_trans_items = -1; +static int hf_dcerpc_cn_bind_abstract_syntax = -1; +static int hf_dcerpc_cn_bind_if_id = -1; +static int hf_dcerpc_cn_bind_if_ver = -1; +static int hf_dcerpc_cn_bind_if_ver_minor = -1; +static int hf_dcerpc_cn_bind_trans_syntax = -1; +static int hf_dcerpc_cn_bind_trans_id = -1; +static int hf_dcerpc_cn_bind_trans_ver = -1; +static int hf_dcerpc_cn_bind_trans_btfn = -1; +static int hf_dcerpc_cn_bind_trans_btfn_01 = -1; +static int hf_dcerpc_cn_bind_trans_btfn_02 = -1; +static int hf_dcerpc_cn_alloc_hint = -1; +static int hf_dcerpc_cn_sec_addr_len = -1; +static int hf_dcerpc_cn_sec_addr = -1; +static int hf_dcerpc_cn_num_results = -1; +static int hf_dcerpc_cn_ack_result = -1; +static int hf_dcerpc_cn_ack_reason = -1; +static int hf_dcerpc_cn_ack_trans_id = -1; +static int hf_dcerpc_cn_ack_trans_ver = -1; +static int hf_dcerpc_cn_reject_reason = -1; +static int hf_dcerpc_cn_num_protocols = -1; +static int hf_dcerpc_cn_protocol_ver_major = -1; +static int hf_dcerpc_cn_protocol_ver_minor = -1; +static int hf_dcerpc_cn_cancel_count = -1; +static int hf_dcerpc_cn_fault_flags = -1; +static int hf_dcerpc_cn_fault_flags_extended_error_info = -1; +static int hf_dcerpc_cn_status = -1; +static int hf_dcerpc_cn_deseg_req = -1; +static int hf_dcerpc_cn_rts_flags = -1; +static int hf_dcerpc_cn_rts_flags_ping = -1; +static int hf_dcerpc_cn_rts_flags_other_cmd = -1; +static int hf_dcerpc_cn_rts_flags_recycle_channel = -1; +static int hf_dcerpc_cn_rts_flags_in_channel = -1; +static int hf_dcerpc_cn_rts_flags_out_channel = -1; +static int hf_dcerpc_cn_rts_flags_eof = -1; +static int hf_dcerpc_cn_rts_commands_nb = -1; +static int hf_dcerpc_cn_rts_command = -1; +static int hf_dcerpc_cn_rts_command_receivewindowsize = -1; +static int hf_dcerpc_cn_rts_command_fack_bytesreceived = -1; +static int hf_dcerpc_cn_rts_command_fack_availablewindow = -1; +static int hf_dcerpc_cn_rts_command_fack_channelcookie = -1; +static int hf_dcerpc_cn_rts_command_connectiontimeout = -1; +static int hf_dcerpc_cn_rts_command_cookie = -1; +static int hf_dcerpc_cn_rts_command_channellifetime = -1; +static int hf_dcerpc_cn_rts_command_clientkeepalive = -1; +static int hf_dcerpc_cn_rts_command_version = -1; +static int hf_dcerpc_cn_rts_command_conformancecount = -1; +static int hf_dcerpc_cn_rts_command_padding = -1; +static int hf_dcerpc_cn_rts_command_addrtype = -1; +static int hf_dcerpc_cn_rts_command_associationgroupid = -1; +static int hf_dcerpc_cn_rts_command_forwarddestination = -1; +static int hf_dcerpc_cn_rts_command_pingtrafficsentnotify = -1; +static int hf_dcerpc_auth_type = -1; +static int hf_dcerpc_auth_level = -1; +static int hf_dcerpc_auth_pad_len = -1; +static int hf_dcerpc_auth_rsrvd = -1; +static int hf_dcerpc_auth_ctx_id = -1; +static int hf_dcerpc_dg_flags1 = -1; +static int hf_dcerpc_dg_flags1_rsrvd_01 = -1; +static int hf_dcerpc_dg_flags1_last_frag = -1; +static int hf_dcerpc_dg_flags1_frag = -1; +static int hf_dcerpc_dg_flags1_nofack = -1; +static int hf_dcerpc_dg_flags1_maybe = -1; +static int hf_dcerpc_dg_flags1_idempotent = -1; +static int hf_dcerpc_dg_flags1_broadcast = -1; +static int hf_dcerpc_dg_flags1_rsrvd_80 = -1; +static int hf_dcerpc_dg_flags2 = -1; +static int hf_dcerpc_dg_flags2_rsrvd_01 = -1; +static int hf_dcerpc_dg_flags2_cancel_pending = -1; +static int hf_dcerpc_dg_flags2_rsrvd_04 = -1; +static int hf_dcerpc_dg_flags2_rsrvd_08 = -1; +static int hf_dcerpc_dg_flags2_rsrvd_10 = -1; +static int hf_dcerpc_dg_flags2_rsrvd_20 = -1; +static int hf_dcerpc_dg_flags2_rsrvd_40 = -1; +static int hf_dcerpc_dg_flags2_rsrvd_80 = -1; +static int hf_dcerpc_dg_serial_hi = -1; +static int hf_dcerpc_obj_id = -1; +static int hf_dcerpc_dg_if_id = -1; +static int hf_dcerpc_dg_act_id = -1; +static int hf_dcerpc_dg_serial_lo = -1; +static int hf_dcerpc_dg_ahint = -1; +static int hf_dcerpc_dg_ihint = -1; +static int hf_dcerpc_dg_frag_len = -1; +static int hf_dcerpc_dg_frag_num = -1; +static int hf_dcerpc_dg_auth_proto = -1; +static int hf_dcerpc_opnum = -1; +static int hf_dcerpc_dg_seqnum = -1; +static int hf_dcerpc_dg_server_boot = -1; +static int hf_dcerpc_dg_if_ver = -1; +static int hf_dcerpc_krb5_av_prot_level = -1; +static int hf_dcerpc_krb5_av_key_vers_num = -1; +static int hf_dcerpc_krb5_av_key_auth_verifier = -1; +static int hf_dcerpc_dg_cancel_vers = -1; +static int hf_dcerpc_dg_cancel_id = -1; +static int hf_dcerpc_dg_server_accepting_cancels = -1; +static int hf_dcerpc_dg_fack_vers = -1; +static int hf_dcerpc_dg_fack_window_size = -1; +static int hf_dcerpc_dg_fack_max_tsdu = -1; +static int hf_dcerpc_dg_fack_max_frag_size = -1; +static int hf_dcerpc_dg_fack_serial_num = -1; +static int hf_dcerpc_dg_fack_selack_len = -1; +static int hf_dcerpc_dg_fack_selack = -1; +static int hf_dcerpc_dg_status = -1; +static int hf_dcerpc_array_max_count = -1; +static int hf_dcerpc_array_offset = -1; +static int hf_dcerpc_array_actual_count = -1; +static int hf_dcerpc_op = -1; +static int hf_dcerpc_referent_id32 = -1; +static int hf_dcerpc_referent_id64 = -1; +static int hf_dcerpc_null_pointer = -1; +static int hf_dcerpc_fragments = -1; +static int hf_dcerpc_fragment = -1; +static int hf_dcerpc_fragment_overlap = -1; +static int hf_dcerpc_fragment_overlap_conflict = -1; +static int hf_dcerpc_fragment_multiple_tails = -1; +static int hf_dcerpc_fragment_too_long_fragment = -1; +static int hf_dcerpc_fragment_error = -1; +static int hf_dcerpc_fragment_count = -1; +static int hf_dcerpc_reassembled_in = -1; +static int hf_dcerpc_reassembled_length = -1; +static int hf_dcerpc_unknown_if_id = -1; +static int hf_dcerpc_sec_vt_signature = -1; +static int hf_dcerpc_sec_vt_command = -1; +static int hf_dcerpc_sec_vt_command_cmd = -1; +static int hf_dcerpc_sec_vt_command_end = -1; +static int hf_dcerpc_sec_vt_command_must = -1; +static int hf_dcerpc_sec_vt_command_length = -1; +static int hf_dcerpc_sec_vt_bitmask = -1; +static int hf_dcerpc_sec_vt_bitmask_sign = -1; +static int hf_dcerpc_sec_vt_pcontext_uuid = -1; +static int hf_dcerpc_sec_vt_pcontext_ver = -1; + +static int * const sec_vt_command_fields[] = { + &hf_dcerpc_sec_vt_command_cmd, + &hf_dcerpc_sec_vt_command_end, + &hf_dcerpc_sec_vt_command_must, + NULL +}; +static int hf_dcerpc_reserved = -1; +static int hf_dcerpc_unknown = -1; +static int hf_dcerpc_missalign = -1; + +/* Generated from convert_proto_tree_add_text.pl */ +static int hf_dcerpc_duplicate_ptr = -1; +static int hf_dcerpc_encrypted_stub_data = -1; +static int hf_dcerpc_decrypted_stub_data = -1; +static int hf_dcerpc_payload_stub_data = -1; +static int hf_dcerpc_stub_data_with_sec_vt = -1; +static int hf_dcerpc_stub_data = -1; +static int hf_dcerpc_auth_padding = -1; +static int hf_dcerpc_auth_info = -1; +static int hf_dcerpc_auth_credentials = -1; +static int hf_dcerpc_fault_stub_data = -1; +static int hf_dcerpc_fragment_data = -1; +static int hf_dcerpc_cmd_client_ipv4 = -1; +static int hf_dcerpc_cmd_client_ipv6 = -1; +static int hf_dcerpc_authentication_verifier = -1; + +static int * const dcerpc_cn_bind_trans_btfn_fields[] = { + &hf_dcerpc_cn_bind_trans_btfn_01, + &hf_dcerpc_cn_bind_trans_btfn_02, + NULL +}; + +static int * const sec_vt_bitmask_fields[] = { + &hf_dcerpc_sec_vt_bitmask_sign, + NULL +}; + +static int * const dcerpc_cn_fault_flags_fields[] = { + &hf_dcerpc_cn_fault_flags_extended_error_info, + NULL +}; + +static const value_string sec_vt_command_cmd_vals[] = { + {1, "BITMASK_1"}, + {2, "PCONTEXT"}, + {3, "HEADER2"}, + {0, NULL} +}; + +static gint ett_dcerpc = -1; +static gint ett_dcerpc_cn_flags = -1; +static gint ett_dcerpc_cn_ctx = -1; +static gint ett_dcerpc_cn_iface = -1; +static gint ett_dcerpc_cn_trans_syntax = -1; +static gint ett_dcerpc_cn_trans_btfn = -1; +static gint ett_dcerpc_cn_bind_trans_btfn = -1; +static gint ett_dcerpc_cn_rts_flags = -1; +static gint ett_dcerpc_cn_rts_command = -1; +static gint ett_dcerpc_cn_rts_pdu = -1; +static gint ett_dcerpc_drep = -1; +static gint ett_dcerpc_dg_flags1 = -1; +static gint ett_dcerpc_dg_flags2 = -1; +static gint ett_dcerpc_pointer_data = -1; +static gint ett_dcerpc_string = -1; +static gint ett_dcerpc_fragments = -1; +static gint ett_dcerpc_fragment = -1; +static gint ett_dcerpc_krb5_auth_verf = -1; +static gint ett_dcerpc_auth_info = -1; +static gint ett_dcerpc_verification_trailer = -1; +static gint ett_dcerpc_sec_vt_command = -1; +static gint ett_dcerpc_sec_vt_bitmask = -1; +static gint ett_dcerpc_sec_vt_pcontext = -1; +static gint ett_dcerpc_sec_vt_header = -1; +static gint ett_dcerpc_complete_stub_data = -1; +static gint ett_dcerpc_fault_flags = -1; +static gint ett_dcerpc_fault_stub_data = -1; + +static expert_field ei_dcerpc_fragment_multiple = EI_INIT; +static expert_field ei_dcerpc_cn_status = EI_INIT; +static expert_field ei_dcerpc_fragment_reassembled = EI_INIT; +static expert_field ei_dcerpc_fragment = EI_INIT; +static expert_field ei_dcerpc_no_request_found = EI_INIT; +/* static expert_field ei_dcerpc_context_change = EI_INIT; */ +static expert_field ei_dcerpc_cn_ctx_id_no_bind = EI_INIT; +static expert_field ei_dcerpc_bind_not_acknowledged = EI_INIT; +static expert_field ei_dcerpc_verifier_unavailable = EI_INIT; +static expert_field ei_dcerpc_invalid_pdu_authentication_attempt = EI_INIT; +/* Generated from convert_proto_tree_add_text.pl */ +static expert_field ei_dcerpc_long_frame = EI_INIT; +static expert_field ei_dcerpc_cn_rts_command = EI_INIT; +static expert_field ei_dcerpc_not_implemented = EI_INIT; + +static const guint8 TRAILER_SIGNATURE[] = {0x8a, 0xe3, 0x13, 0x71, 0x02, 0xf4, 0x36, 0x71}; +static tvbuff_t *tvb_trailer_signature = NULL; + +static GSList *decode_dcerpc_bindings = NULL; +/* + * To keep track of ctx_id mappings. + * + * Every time we see a bind call we update this table. + * Note that we always specify a SMB FID. For non-SMB transports this + * value is 0. + */ +static wmem_map_t *dcerpc_binds = NULL; + +typedef struct _dcerpc_bind_key { + conversation_t *conv; + guint16 ctx_id; + guint64 transport_salt; +} dcerpc_bind_key; + +typedef struct _dcerpc_bind_value { + e_guid_t uuid; + guint16 ver; + e_guid_t transport; +} dcerpc_bind_value; + +static wmem_map_t *dcerpc_auths = NULL; + +typedef struct _dcerpc_auth_context { + conversation_t *conv; + guint64 transport_salt; + guint8 auth_type; + guint8 auth_level; + guint32 auth_context_id; + guint32 first_frame; + gboolean hdr_signing; +} dcerpc_auth_context; + +/* Extra data for DCERPC handling and tracking of context ids */ +typedef struct _dcerpc_decode_as_data { + guint16 dcectxid; /**< Context ID (DCERPC-specific) */ + int dcetransporttype; /**< Transport type + * Value -1 means "not a DCERPC packet" + */ + guint64 dcetransportsalt; /**< fid: if transporttype==DCE_CN_TRANSPORT_SMBPIPE */ +} dcerpc_decode_as_data; + +static dcerpc_decode_as_data* +dcerpc_get_decode_data(packet_info* pinfo) +{ + dcerpc_decode_as_data* data = (dcerpc_decode_as_data*)p_get_proto_data(pinfo->pool, pinfo, proto_dcerpc, 0); + if (data == NULL) + { + data = wmem_new0(pinfo->pool, dcerpc_decode_as_data); + data->dcetransporttype = -1; + p_add_proto_data(pinfo->pool, pinfo, proto_dcerpc, 0, data); + } + + return data; +} + +/** + * Registers a conversation/UUID binding association, so that + * we can invoke the proper sub-dissector for a given DCERPC + * conversation. + * + * @param binding all values needed to create and bind a new conversation + * + * @return Pointer to newly-added UUID/conversation binding. + */ +static struct _dcerpc_bind_value * +dcerpc_add_conv_to_bind_table(decode_dcerpc_bind_values_t *binding) +{ + dcerpc_bind_value *bind_value; + dcerpc_bind_key *key; + conversation_t *conv; + + conv = find_conversation( + 0, + &binding->addr_a, + &binding->addr_b, + conversation_pt_to_conversation_type(binding->ptype), + binding->port_a, + binding->port_b, + 0); + + if (!conv) { + conv = conversation_new( + 0, + &binding->addr_a, + &binding->addr_b, + conversation_pt_to_conversation_type(binding->ptype), + binding->port_a, + binding->port_b, + 0); + } + + bind_value = wmem_new(wmem_file_scope(), dcerpc_bind_value); + bind_value->uuid = binding->uuid; + bind_value->ver = binding->ver; + /* For now, assume all DCE/RPC we pick from "decode as" is using + standard ndr and not ndr64. + We should make this selectable from the dialog in the future + */ + bind_value->transport = uuid_data_repr_proto; + + key = wmem_new(wmem_file_scope(), dcerpc_bind_key); + key->conv = conv; + key->ctx_id = binding->ctx_id; + key->transport_salt = binding->transport_salt; + + /* add this entry to the bind table */ + wmem_map_insert(dcerpc_binds, key, bind_value); + + return bind_value; + +} + +/* inject one of our bindings into the dcerpc binding table */ +static void +decode_dcerpc_inject_binding(gpointer data, gpointer user_data _U_) +{ + dcerpc_add_conv_to_bind_table((decode_dcerpc_bind_values_t *) data); +} + +/* inject all of our bindings into the dcerpc binding table */ +static void +decode_dcerpc_inject_bindings(void) { + g_slist_foreach(decode_dcerpc_bindings, decode_dcerpc_inject_binding, NULL /* user_data */); +} + +/* free a binding */ +static void +decode_dcerpc_binding_free(void *binding_in) +{ + decode_dcerpc_bind_values_t *binding = (decode_dcerpc_bind_values_t *)binding_in; + + free_address(&binding->addr_a); + free_address(&binding->addr_b); + if (binding->ifname) + g_string_free(binding->ifname, TRUE); + g_free(binding); +} + +static void +dcerpc_decode_as_free(gpointer value) +{ + decode_dcerpc_bind_values_t *binding = (decode_dcerpc_bind_values_t *)value; + if (binding != NULL) + decode_dcerpc_binding_free(binding); +} + +/* removes all bindings */ +void +decode_dcerpc_reset_all(void) +{ + decode_dcerpc_bind_values_t *binding; + + while (decode_dcerpc_bindings) { + binding = (decode_dcerpc_bind_values_t *)decode_dcerpc_bindings->data; + + decode_dcerpc_bindings = g_slist_remove( + decode_dcerpc_bindings, + decode_dcerpc_bindings->data); + decode_dcerpc_binding_free(binding); + } +} + + +void +decode_dcerpc_add_show_list(decode_add_show_list_func func, gpointer user_data) +{ + g_slist_foreach(decode_dcerpc_bindings, func, user_data); +} + +static void +dcerpc_prompt(packet_info *pinfo, gchar* result) +{ + GString *str = g_string_new("Replace binding between:\r\n"), + *address_str = g_string_new(""); + dcerpc_decode_as_data* decode_data = dcerpc_get_decode_data(pinfo); + + switch (pinfo->ptype) { + case(PT_TCP): + g_string_append(address_str, "Address: ToBeDone TCP port"); + break; + case(PT_UDP): + g_string_append(address_str, "Address: ToBeDone UDP port"); + break; + default: + g_string_append(address_str, "Address: ToBeDone Unknown port type"); + } + + g_string_append_printf(str, "%s: %u\r\n", address_str->str, pinfo->srcport); + g_string_append(str, "&\r\n"); + g_string_append_printf(str, "%s: %u\r\n", address_str->str, pinfo->destport); + g_string_append_printf(str, "&\r\nContext ID: %u\r\n", decode_data->dcectxid); + g_string_append_printf(str, "&\r\nSMB FID: %"PRIu64"\r\n", + dcerpc_get_transport_salt(pinfo)); + g_string_append(str, "with:\r\n"); + + (void) g_strlcpy(result, str->str, MAX_DECODE_AS_PROMPT_LEN); + g_string_free(str, TRUE); + g_string_free(address_str, TRUE); +} + +static gpointer +dcerpc_value(packet_info *pinfo) +{ + decode_dcerpc_bind_values_t *binding; + dcerpc_decode_as_data* decode_data = dcerpc_get_decode_data(pinfo); + + /* clone binding */ + binding = g_new(decode_dcerpc_bind_values_t,1); + copy_address(&binding->addr_a, &pinfo->src); + copy_address(&binding->addr_b, &pinfo->dst); + binding->ptype = pinfo->ptype; + binding->port_a = pinfo->srcport; + binding->port_b = pinfo->destport; + binding->ctx_id = decode_data->dcectxid; + binding->transport_salt = dcerpc_get_transport_salt(pinfo); + binding->ifname = NULL; + /*binding->uuid = NULL;*/ + binding->ver = 0; + + return binding; +} + +struct dcerpc_decode_as_populate +{ + decode_as_add_to_list_func add_to_list; + gpointer ui_element; +}; + +static void +decode_dcerpc_add_to_list(gpointer key, gpointer value, gpointer user_data) +{ + struct dcerpc_decode_as_populate* populate = (struct dcerpc_decode_as_populate*)user_data; + + /*guid_key *k = key;*/ + dcerpc_uuid_value *v = (dcerpc_uuid_value *)value; + + if (strcmp(v->name, "(none)")) + populate->add_to_list("DCE-RPC", v->name, key, populate->ui_element); +} + +static void +dcerpc_populate_list(const gchar *table_name _U_, decode_as_add_to_list_func add_to_list, gpointer ui_element) +{ + struct dcerpc_decode_as_populate populate; + + populate.add_to_list = add_to_list; + populate.ui_element = ui_element; + + g_hash_table_foreach(dcerpc_uuids, decode_dcerpc_add_to_list, &populate); +} + +/* compare two bindings (except the interface related things, e.g. uuid) */ +static gint +decode_dcerpc_binding_cmp(gconstpointer a, gconstpointer b) +{ + const decode_dcerpc_bind_values_t *binding_a = (const decode_dcerpc_bind_values_t *)a; + const decode_dcerpc_bind_values_t *binding_b = (const decode_dcerpc_bind_values_t *)b; + + + /* don't compare uuid and ver! */ + if ( + addresses_equal(&binding_a->addr_a, &binding_b->addr_a) && + addresses_equal(&binding_a->addr_b, &binding_b->addr_b) && + binding_a->ptype == binding_b->ptype && + binding_a->port_a == binding_b->port_a && + binding_a->port_b == binding_b->port_b && + binding_a->ctx_id == binding_b->ctx_id && + binding_a->transport_salt == binding_b->transport_salt) + { + /* equal */ + return 0; + } + + /* unequal */ + return 1; +} + +/* remove a binding (looking the same way as the given one) */ +static gboolean +decode_dcerpc_binding_reset(const char *name _U_, gconstpointer pattern) +{ + const decode_dcerpc_bind_values_t *binding = (const decode_dcerpc_bind_values_t *)pattern; + GSList *le; + decode_dcerpc_bind_values_t *old_binding; + + /* find the old binding (if it exists) */ + le = g_slist_find_custom(decode_dcerpc_bindings, + binding, + decode_dcerpc_binding_cmp); + if (le == NULL) + return FALSE; + + old_binding = (decode_dcerpc_bind_values_t *)le->data; + + decode_dcerpc_bindings = g_slist_remove(decode_dcerpc_bindings, le->data); + + free_address(&old_binding->addr_a); + free_address(&old_binding->addr_b); + g_string_free(old_binding->ifname, TRUE); + g_free(old_binding); + return FALSE; +} + +static gboolean +dcerpc_decode_as_change(const char *name, gconstpointer pattern, gconstpointer handle, const gchar* list_name) +{ + const decode_dcerpc_bind_values_t *binding = (const decode_dcerpc_bind_values_t*)pattern; + decode_dcerpc_bind_values_t *stored_binding; + const guid_key *key = (const guid_key *)handle; + + /* remove a probably existing old binding */ + decode_dcerpc_binding_reset(name, binding); + + /* + * Clone the new binding, update the changing parts, and append it + * to the list. + */ + stored_binding = g_new(decode_dcerpc_bind_values_t,1); + *stored_binding = *binding; + copy_address(&stored_binding->addr_a, &binding->addr_a); + copy_address(&stored_binding->addr_b, &binding->addr_b); + stored_binding->ifname = g_string_new(list_name); + stored_binding->uuid = key->guid; + stored_binding->ver = key->ver; + + decode_dcerpc_bindings = g_slist_append (decode_dcerpc_bindings, stored_binding); + + return FALSE; +} + +static const fragment_items dcerpc_frag_items = { + &ett_dcerpc_fragments, + &ett_dcerpc_fragment, + + &hf_dcerpc_fragments, + &hf_dcerpc_fragment, + &hf_dcerpc_fragment_overlap, + &hf_dcerpc_fragment_overlap_conflict, + &hf_dcerpc_fragment_multiple_tails, + &hf_dcerpc_fragment_too_long_fragment, + &hf_dcerpc_fragment_error, + &hf_dcerpc_fragment_count, + NULL, + &hf_dcerpc_reassembled_length, + /* Reassembled data field */ + NULL, + "fragments" +}; + +/* try to desegment big DCE/RPC packets over TCP? */ +static gboolean dcerpc_cn_desegment = TRUE; + +/* reassemble DCE/RPC fragments */ +/* reassembly of cl dcerpc fragments will not work for the case where ONE frame + might contain multiple dcerpc fragments for different PDUs. + this case would be so unusual/weird so if you got captures like that: + too bad + + reassembly of co dcerpc fragments will not work for the case where TCP/SMB frames + are coming in out of sequence, but that will hurt in a lot of other places as well. +*/ +static gboolean dcerpc_reassemble = TRUE; +static reassembly_table dcerpc_co_reassembly_table; +static reassembly_table dcerpc_cl_reassembly_table; + +typedef struct _dcerpc_fragment_key { + address src; + address dst; + guint32 id; + e_guid_t act_id; +} dcerpc_fragment_key; + +static guint +dcerpc_fragment_hash(gconstpointer k) +{ + const dcerpc_fragment_key* key = (const dcerpc_fragment_key*) k; + guint hash_val; + + hash_val = 0; + + hash_val += key->id; + hash_val += key->act_id.data1; + hash_val += key->act_id.data2 << 16; + hash_val += key->act_id.data3; + + return hash_val; +} + +static gint +dcerpc_fragment_equal(gconstpointer k1, gconstpointer k2) +{ + const dcerpc_fragment_key* key1 = (const dcerpc_fragment_key*) k1; + const dcerpc_fragment_key* key2 = (const dcerpc_fragment_key*) k2; + + /*key.id is the first item to compare since item is most + likely to differ between sessions, thus shortcircuiting + the comparison of addresses. + */ + return (((key1->id == key2->id) + && (addresses_equal(&key1->src, &key2->src)) + && (addresses_equal(&key1->dst, &key2->dst)) + && (memcmp (&key1->act_id, &key2->act_id, sizeof (e_guid_t)) == 0)) + ? TRUE : FALSE); +} + +/* allocate a persistent dcerpc fragment key to insert in the hash */ +static void * +dcerpc_fragment_temporary_key(const packet_info *pinfo, const guint32 id, + const void *data) +{ + dcerpc_fragment_key *key = g_slice_new(dcerpc_fragment_key); + const e_dce_dg_common_hdr_t *hdr = (const e_dce_dg_common_hdr_t *)data; + + copy_address_shallow(&key->src, &pinfo->src); + copy_address_shallow(&key->dst, &pinfo->dst); + key->id = id; + key->act_id = hdr->act_id; + + return key; +} + +/* allocate a persistent dcerpc fragment key to insert in the hash */ +static void * +dcerpc_fragment_persistent_key(const packet_info *pinfo, const guint32 id, + const void *data) +{ + dcerpc_fragment_key *key = g_slice_new(dcerpc_fragment_key); + const e_dce_dg_common_hdr_t *hdr = (const e_dce_dg_common_hdr_t *)data; + + copy_address(&key->src, &pinfo->src); + copy_address(&key->dst, &pinfo->dst); + key->id = id; + key->act_id = hdr->act_id; + + return key; +} + +static void +dcerpc_fragment_free_temporary_key(gpointer ptr) +{ + dcerpc_fragment_key *key = (dcerpc_fragment_key *)ptr; + + g_slice_free(dcerpc_fragment_key, key); +} + +static void +dcerpc_fragment_free_persistent_key(gpointer ptr) +{ + dcerpc_fragment_key *key = (dcerpc_fragment_key *)ptr; + + if (key) { + /* + * Free up the copies of the addresses from the old key. + */ + free_address(&key->src); + free_address(&key->dst); + + g_slice_free(dcerpc_fragment_key, key); + } +} + +static const reassembly_table_functions dcerpc_cl_reassembly_table_functions = { + dcerpc_fragment_hash, + dcerpc_fragment_equal, + dcerpc_fragment_temporary_key, + dcerpc_fragment_persistent_key, + dcerpc_fragment_free_temporary_key, + dcerpc_fragment_free_persistent_key +}; + +/* + * Authentication subdissectors. Used to dissect authentication blobs in + * DCERPC binds, requests and responses. + */ + +typedef struct _dcerpc_auth_subdissector { + guint8 auth_level; + guint8 auth_type; + dcerpc_auth_subdissector_fns auth_fns; +} dcerpc_auth_subdissector; + +static GSList *dcerpc_auth_subdissector_list; + +static dcerpc_auth_subdissector_fns *get_auth_subdissector_fns( + guint8 auth_level, guint8 auth_type) +{ + gpointer data; + int i; + + for (i = 0; (data = g_slist_nth_data(dcerpc_auth_subdissector_list, i)); i++) { + dcerpc_auth_subdissector *asd = (dcerpc_auth_subdissector *)data; + + if ((asd->auth_level == auth_level) && + (asd->auth_type == auth_type)) + return &asd->auth_fns; + } + + return NULL; +} + +void register_dcerpc_auth_subdissector(guint8 auth_level, guint8 auth_type, + dcerpc_auth_subdissector_fns *fns) +{ + dcerpc_auth_subdissector *d; + + if (get_auth_subdissector_fns(auth_level, auth_type)) + return; + + d = g_new(dcerpc_auth_subdissector, 1); + + d->auth_level = auth_level; + d->auth_type = auth_type; + d->auth_fns = *fns; + + dcerpc_auth_subdissector_list = g_slist_append(dcerpc_auth_subdissector_list, d); +} + +/* Hand off verifier data to a registered dissector */ + +static void dissect_auth_verf(packet_info *pinfo, + e_dce_cn_common_hdr_t *hdr, + dcerpc_auth_info *auth_info) +{ + dcerpc_dissect_fnct_t *fn = NULL; + /* XXX - "stub" a fake DCERPC INFO STRUCTURE + If a dcerpc_info is really needed, update + the call stacks to include it + */ + FAKE_DCERPC_INFO_STRUCTURE + + if (auth_info == NULL) { + return; + } + + if (auth_info->auth_fns == NULL) { + return; + } + + switch (hdr->ptype) { + case PDU_BIND: + case PDU_ALTER: + fn = auth_info->auth_fns->bind_fn; + break; + case PDU_BIND_ACK: + case PDU_ALTER_ACK: + fn = auth_info->auth_fns->bind_ack_fn; + break; + case PDU_AUTH3: + fn = auth_info->auth_fns->auth3_fn; + break; + case PDU_REQ: + case PDU_CO_CANCEL: + case PDU_ORPHANED: + fn = auth_info->auth_fns->req_verf_fn; + break; + case PDU_RESP: + case PDU_FAULT: + fn = auth_info->auth_fns->resp_verf_fn; + break; + + default: + /* Don't know how to handle authentication data in this + pdu type. */ + proto_tree_add_expert_format(auth_info->auth_tree, pinfo, + &ei_dcerpc_invalid_pdu_authentication_attempt, + auth_info->auth_tvb, 0, 0, + "Don't know how to dissect authentication data for %s pdu type", + val_to_str(hdr->ptype, pckt_vals, "Unknown (%u)")); + return; + break; + } + + if (fn) + fn(auth_info->auth_tvb, 0, pinfo, auth_info->auth_tree, &di, hdr->drep); + else + proto_tree_add_expert_format(auth_info->auth_tree, pinfo, + &ei_dcerpc_verifier_unavailable, + auth_info->auth_tvb, 0, hdr->auth_len, + "%s Verifier unavailable", + val_to_str(auth_info->auth_type, + authn_protocol_vals, + "Unknown (%u)")); +} + +static proto_item* +proto_tree_add_dcerpc_drep(proto_tree *tree, tvbuff_t *tvb, int offset, guint8 drep[], int drep_len) +{ + const guint8 byteorder = drep[0] >> 4; + const guint8 character = drep[0] & 0x0f; + const guint8 fp = drep[1]; + proto_item *ti = proto_tree_add_bytes(tree, hf_dcerpc_drep, tvb, offset, drep_len, drep); + proto_tree *tr = proto_item_add_subtree(ti, ett_dcerpc_drep); + + proto_tree_add_uint(tr, hf_dcerpc_drep_byteorder, tvb, offset, 1, byteorder); + proto_tree_add_uint(tr, hf_dcerpc_drep_character, tvb, offset, 1, character); + proto_tree_add_uint(tr, hf_dcerpc_drep_fp, tvb, offset+1, 1, fp); + + proto_item_append_text(ti, " (Order: %s, Char: %s, Float: %s)", + val_to_str(byteorder, drep_byteorder_vals, "Unknown (%u)"), + val_to_str(character, drep_character_vals, "Unknown (%u)"), + val_to_str(fp, drep_fp_vals, "Unknown (%u)")); + return ti; +} + +/* Hand off payload data to a registered dissector */ + +static tvbuff_t *decode_encrypted_data(tvbuff_t *header_tvb, + tvbuff_t *payload_tvb, + tvbuff_t *trailer_tvb, + packet_info *pinfo, + e_dce_cn_common_hdr_t *hdr, + dcerpc_auth_info *auth_info) +{ + dcerpc_decode_data_fnct_t *fn = NULL; + + if (auth_info == NULL) + return NULL; + + if (auth_info->auth_fns == NULL) + return NULL; + + switch (hdr->ptype) { + case PDU_REQ: + fn = auth_info->auth_fns->req_data_fn; + break; + case PDU_RESP: + case PDU_FAULT: + fn = auth_info->auth_fns->resp_data_fn; + break; + } + + if (fn) + return fn(header_tvb, payload_tvb, trailer_tvb, auth_info->auth_tvb, pinfo, auth_info); + + return NULL; +} + +typedef struct _dcerpc_dissector_data +{ + dcerpc_uuid_value *sub_proto; + dcerpc_info *info; + gboolean decrypted; + dcerpc_auth_info *auth_info; + guint8 *drep; + proto_tree *dcerpc_tree; +} dcerpc_dissector_data_t; + +/* + * Subdissectors + */ + +static dissector_table_t uuid_dissector_table; + +/* the registered subdissectors */ +GHashTable *dcerpc_uuids = NULL; + +static gint +dcerpc_uuid_equal(gconstpointer k1, gconstpointer k2) +{ + const guid_key *key1 = (const guid_key *)k1; + const guid_key *key2 = (const guid_key *)k2; + return ((memcmp(&key1->guid, &key2->guid, sizeof (e_guid_t)) == 0) + && (key1->ver == key2->ver)); +} + +static guint +dcerpc_uuid_hash(gconstpointer k) +{ + const guid_key *key = (const guid_key *)k; + /* This isn't perfect, but the Data1 part of these is almost always + unique. */ + return key->guid.data1; +} + + +static int +dissect_verification_trailer(packet_info *pinfo, tvbuff_t *tvb, int stub_offset, + proto_tree *parent_tree, int *signature_offset); + +static void +show_stub_data(packet_info *pinfo, tvbuff_t *tvb, gint offset, proto_tree *dcerpc_tree, + dcerpc_auth_info *auth_info, gboolean is_encrypted) +{ + int length, plain_length, auth_pad_len; + guint auth_pad_offset; + + /* + * We don't show stub data unless we have some in the tvbuff; + * however, in the protocol tree, we show, as the number of + * bytes, the reported number of bytes, not the number of bytes + * that happen to be in the tvbuff. + */ + if (tvb_reported_length_remaining(tvb, offset) > 0) { + auth_pad_len = auth_info?auth_info->auth_pad_len:0; + length = tvb_reported_length_remaining(tvb, offset); + + /* if auth_pad_len is larger than length then we ignore auth_pad_len totally */ + plain_length = length - auth_pad_len; + if (plain_length < 1) { + plain_length = length; + auth_pad_len = 0; + } + auth_pad_offset = offset + plain_length; + + if ((auth_info != NULL) && + (auth_info->auth_level == DCE_C_AUTHN_LEVEL_PKT_PRIVACY)) { + if (is_encrypted) { + proto_tree_add_item(dcerpc_tree, hf_dcerpc_encrypted_stub_data, tvb, offset, length, ENC_NA); + /* is the padding is still inside the encrypted blob, don't display it explicit */ + auth_pad_len = 0; + } else { + proto_tree_add_item(dcerpc_tree, hf_dcerpc_decrypted_stub_data, tvb, offset, plain_length, ENC_NA); + dissect_verification_trailer(pinfo, tvb, offset, dcerpc_tree, NULL); + } + } else { + proto_tree_add_item(dcerpc_tree, hf_dcerpc_stub_data, tvb, offset, plain_length, ENC_NA); + dissect_verification_trailer(pinfo, tvb, offset, dcerpc_tree, NULL); + } + /* If there is auth padding at the end of the stub, display it */ + if (auth_pad_len != 0) { + proto_tree_add_item(dcerpc_tree, hf_dcerpc_auth_padding, tvb, auth_pad_offset, auth_pad_len, ENC_NA); + } + } +} + +static int +dissect_dcerpc_guid(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) +{ + dcerpc_dissector_data_t* dissector_data = (dcerpc_dissector_data_t*)data; + const gchar *name = NULL; + dcerpc_sub_dissector *proc; + int (*volatile sub_dissect)(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree, dcerpc_info *di, guint8 *drep) = NULL; + proto_item *pi, *sub_item; + proto_tree *sub_tree; + volatile guint length; + guint reported_length; + volatile gint offset = 0; + tvbuff_t *volatile stub_tvb; + tvbuff_t *volatile payload_tvb = NULL; + volatile guint auth_pad_len; + volatile int auth_pad_offset; + const char *volatile saved_proto; + + for (proc = dissector_data->sub_proto->procs; proc->name; proc++) { + if (proc->num == dissector_data->info->call_data->opnum) { + name = proc->name; + break; + } + } + + col_set_str(pinfo->cinfo, COL_PROTOCOL, dissector_data->sub_proto->name); + + if (!name) + col_add_fstr(pinfo->cinfo, COL_INFO, "Unknown operation %u %s", + dissector_data->info->call_data->opnum, + (dissector_data->info->ptype == PDU_REQ) ? "request" : "response"); + else + col_add_fstr(pinfo->cinfo, COL_INFO, "%s %s", + name, (dissector_data->info->ptype == PDU_REQ) ? "request" : "response"); + + sub_dissect = (dissector_data->info->ptype == PDU_REQ) ? + proc->dissect_rqst : proc->dissect_resp; + + sub_item = proto_tree_add_item(tree, dissector_data->sub_proto->proto_id, + tvb,//(decrypted_tvb != NULL)?decrypted_tvb:tvb, + 0, -1, ENC_NA); + sub_tree = proto_item_add_subtree(sub_item, dissector_data->sub_proto->ett); + if (!name) + proto_item_append_text(sub_item, ", unknown operation %u", + dissector_data->info->call_data->opnum); + else + proto_item_append_text(sub_item, ", %s", name); + + if (tree) { + /* + * Put the operation number into the tree along with + * the operation's name. + */ + if (dissector_data->sub_proto->opnum_hf != -1) + proto_tree_add_uint_format(sub_tree, dissector_data->sub_proto->opnum_hf, + tvb, 0, 0, dissector_data->info->call_data->opnum, + "Operation: %s (%u)", + name ? name : "Unknown operation", + dissector_data->info->call_data->opnum); + else + proto_tree_add_uint_format_value(sub_tree, hf_dcerpc_op, tvb, + 0, 0, dissector_data->info->call_data->opnum, + "%s (%u)", + name ? name : "Unknown operation", + dissector_data->info->call_data->opnum); + + if ((dissector_data->info->ptype == PDU_REQ) && (dissector_data->info->call_data->rep_frame != 0)) { + pi = proto_tree_add_uint(sub_tree, hf_dcerpc_response_in, + tvb, 0, 0, dissector_data->info->call_data->rep_frame); + proto_item_set_generated(pi); + } + if ((dissector_data->info->ptype == PDU_RESP) && (dissector_data->info->call_data->req_frame != 0)) { + pi = proto_tree_add_uint(sub_tree, hf_dcerpc_request_in, + tvb, 0, 0, dissector_data->info->call_data->req_frame); + proto_item_set_generated(pi); + } + } /* tree */ + + if (!dissector_data->decrypted || (sub_dissect == NULL)) + { + show_stub_data(pinfo, tvb, 0, sub_tree, dissector_data->auth_info, !dissector_data->decrypted); + return tvb_captured_length(tvb); + } + + /* Either there was no encryption or we successfully decrypted + the encrypted payload. */ + + /* We have a subdissector - call it. */ + saved_proto = pinfo->current_proto; + pinfo->current_proto = dissector_data->sub_proto->name; + + init_ndr_pointer_list(dissector_data->info); + + length = tvb_captured_length(tvb); + reported_length = tvb_reported_length(tvb); + + /* + * Remove the authentication padding from the stub data. + */ + if ((dissector_data->auth_info != NULL) && (dissector_data->auth_info->auth_pad_len != 0)) { + if (reported_length >= dissector_data->auth_info->auth_pad_len) { + /* + * OK, the padding length isn't so big that it + * exceeds the stub length. Trim the reported + * length of the tvbuff. + */ + reported_length -= dissector_data->auth_info->auth_pad_len; + + /* + * If that exceeds the actual amount of data in + * the tvbuff (which means we have at least one + * byte of authentication padding in the tvbuff), + * trim the actual amount. + */ + if (length > reported_length) + length = reported_length; + + stub_tvb = tvb_new_subset_length_caplen(tvb, 0, length, reported_length); + auth_pad_len = dissector_data->auth_info->auth_pad_len; + auth_pad_offset = reported_length; + } else { + /* + * The padding length exceeds the stub length. + * Don't bother dissecting the stub, trim the padding + * length to what's in the stub data, and show the + * entire stub as authentication padding. + */ + stub_tvb = NULL; + auth_pad_len = reported_length; + auth_pad_offset = 0; + length = 0; + } + } else { + /* + * No authentication padding. + */ + stub_tvb = tvb; + auth_pad_len = 0; + auth_pad_offset = 0; + } + + if (sub_item) { + proto_item_set_len(sub_item, length); + } + + if (stub_tvb != NULL) { + /* + * Catch all exceptions other than BoundsError, so that even + * if the stub data is bad, we still show the authentication + * padding, if any. + * + * If we get BoundsError, it means the frame was cut short + * by a snapshot length, so there's nothing more to + * dissect; just re-throw that exception. + */ + TRY { + proto_tree *stub_tree = NULL; + int remaining; + int trailer_start_offset = -1; + int trailer_end_offset = -1; + + stub_tree = proto_tree_add_subtree_format(dissector_data->dcerpc_tree, + stub_tvb, 0, length, + ett_dcerpc_complete_stub_data, NULL, + "Complete stub data (%d byte%s)", length, + plurality(length, "", "s")); + trailer_end_offset = dissect_verification_trailer(pinfo, + stub_tvb, 0, + stub_tree, + &trailer_start_offset); + + if (trailer_end_offset != -1) { + remaining = tvb_captured_length_remaining(stub_tvb, + trailer_start_offset); + length -= remaining; + + if (sub_item) { + proto_item_set_len(sub_item, length); + } + } else { + proto_item *payload_item; + + payload_item = proto_tree_add_item(stub_tree, + hf_dcerpc_payload_stub_data, + stub_tvb, 0, length, ENC_NA); + proto_item_append_text(payload_item, " (%d byte%s)", + length, plurality(length, "", "s")); + } + + payload_tvb = tvb_new_subset_length_caplen(stub_tvb, 0, length, length); + offset = sub_dissect(payload_tvb, 0, pinfo, sub_tree, + dissector_data->info, dissector_data->drep); + + /* If we have a subdissector and it didn't dissect all + data in the tvb, make a note of it. */ + remaining = tvb_reported_length_remaining(stub_tvb, offset); + + if (trailer_end_offset != -1) { + if (offset > trailer_start_offset) { + remaining = offset - trailer_start_offset; + proto_tree_add_item(sub_tree, hf_dcerpc_stub_data_with_sec_vt, + stub_tvb, trailer_start_offset, remaining, ENC_NA); + col_append_fstr(pinfo->cinfo, COL_INFO, + "[Payload with Verification Trailer (%d byte%s)]", + remaining, + plurality(remaining, "", "s")); + remaining = 0; + } else { + remaining = trailer_start_offset - offset; + } + } + + if (remaining > 0) { + proto_tree_add_expert(sub_tree, pinfo, &ei_dcerpc_long_frame, stub_tvb, offset, remaining); + col_append_fstr(pinfo->cinfo, COL_INFO, + "[Long frame (%d byte%s)]", + remaining, + plurality(remaining, "", "s")); + } + } CATCH_NONFATAL_ERRORS { + /* + * Somebody threw an exception that means that there + * was a problem dissecting the payload; that means + * that a dissector was found, so we don't need to + * dissect the payload as data or update the protocol + * or info columns. + * + * Just show the exception and then drive on to show + * the authentication padding. + */ + show_exception(stub_tvb, pinfo, tree, EXCEPT_CODE, GET_MESSAGE); + } ENDTRY; + } + + /* If there is auth padding at the end of the stub, display it */ + if (auth_pad_len != 0) { + proto_tree_add_item(sub_tree, hf_dcerpc_auth_padding, tvb, auth_pad_offset, auth_pad_len, ENC_NA); + } + + pinfo->current_proto = saved_proto; + + return tvb_captured_length(tvb); +} + +void +dcerpc_init_uuid(int proto, int ett, e_guid_t *uuid, guint16 ver, + dcerpc_sub_dissector *procs, int opnum_hf) +{ + guid_key *key = (guid_key *)g_malloc(sizeof (*key)); + dcerpc_uuid_value *value = (dcerpc_uuid_value *)g_malloc(sizeof (*value)); + header_field_info *hf_info; + module_t *samr_module; + const char *filter_name = proto_get_protocol_filter_name(proto); + dissector_handle_t guid_handle; + + key->guid = *uuid; + key->ver = ver; + + value->proto = find_protocol_by_id(proto); + value->proto_id = proto; + value->ett = ett; + value->name = proto_get_protocol_short_name(value->proto); + value->procs = procs; + value->opnum_hf = opnum_hf; + + g_hash_table_insert(dcerpc_uuids, key, value); + + hf_info = proto_registrar_get_nth(opnum_hf); + hf_info->strings = value_string_from_subdissectors(procs); + + /* Register the GUID with the dissector table */ + guid_handle = create_dissector_handle( dissect_dcerpc_guid, proto); + dissector_add_guid( "dcerpc.uuid", key, guid_handle ); + + /* add this GUID to the global name resolving */ + guids_add_uuid(uuid, proto_get_protocol_short_name(value->proto)); + + /* Register the samr.nt_password preference as obsolete */ + /* This should be in packet-dcerpc-samr.c */ + if (strcmp(filter_name, "samr") == 0) { + samr_module = prefs_register_protocol_obsolete(proto); + prefs_register_obsolete_preference(samr_module, "nt_password"); + } +} + +/* Function to find the name of a registered protocol + * or NULL if the protocol/version is not known to wireshark. + */ +const char * +dcerpc_get_proto_name(e_guid_t *uuid, guint16 ver) +{ + dissector_handle_t handle; + guid_key key; + + key.guid = *uuid; + key.ver = ver; + + handle = dissector_get_guid_handle(uuid_dissector_table, &key); + if (handle == NULL) { + return NULL; + } + + return dissector_handle_get_protocol_short_name(handle); +} + +/* Function to find the opnum hf-field of a registered protocol + * or -1 if the protocol/version is not known to wireshark. + */ +int +dcerpc_get_proto_hf_opnum(e_guid_t *uuid, guint16 ver) +{ + guid_key key; + dcerpc_uuid_value *sub_proto; + + key.guid = *uuid; + key.ver = ver; + if (!(sub_proto = (dcerpc_uuid_value *)g_hash_table_lookup(dcerpc_uuids, &key))) { + return -1; + } + return sub_proto->opnum_hf; +} + +/* Create a value_string consisting of DCERPC opnum and name from a + subdissector array. */ + +value_string *value_string_from_subdissectors(dcerpc_sub_dissector *sd) +{ + value_string *vs = NULL; + int i; + int num_sd = 0; + +again: + for (i = 0; sd[i].name; i++) { + if (vs) { + vs[i].value = sd[i].num; + vs[i].strptr = sd[i].name; + } else + num_sd++; + } + + if (!vs) { + vs = (value_string *)wmem_alloc(wmem_epan_scope(), (num_sd + 1) * sizeof(value_string)); + goto again; + } + + vs[num_sd].value = 0; + vs[num_sd].strptr = NULL; + + return vs; +} + +/* Function to find the subdissector table of a registered protocol + * or NULL if the protocol/version is not known to wireshark. + */ +dcerpc_sub_dissector * +dcerpc_get_proto_sub_dissector(e_guid_t *uuid, guint16 ver) +{ + guid_key key; + dcerpc_uuid_value *sub_proto; + + key.guid = *uuid; + key.ver = ver; + if (!(sub_proto = (dcerpc_uuid_value *)g_hash_table_lookup(dcerpc_uuids, &key))) { + return NULL; + } + return sub_proto->procs; +} + + + +static gint +dcerpc_bind_equal(gconstpointer k1, gconstpointer k2) +{ + const dcerpc_bind_key *key1 = (const dcerpc_bind_key *)k1; + const dcerpc_bind_key *key2 = (const dcerpc_bind_key *)k2; + return ((key1->conv == key2->conv) + && (key1->ctx_id == key2->ctx_id) + && (key1->transport_salt == key2->transport_salt)); +} + +static guint +dcerpc_bind_hash(gconstpointer k) +{ + const dcerpc_bind_key *key = (const dcerpc_bind_key *)k; + guint hash; + + hash = GPOINTER_TO_UINT(key->conv); + hash += key->ctx_id; + /* sizeof(guint) might be smaller than sizeof(guint64) */ + hash += (guint)key->transport_salt; + hash += (guint)(key->transport_salt << sizeof(guint)); + + return hash; +} + +static gint +dcerpc_auth_context_equal(gconstpointer k1, gconstpointer k2) +{ + const dcerpc_auth_context *key1 = (const dcerpc_auth_context *)k1; + const dcerpc_auth_context *key2 = (const dcerpc_auth_context *)k2; + return ((key1->conv == key2->conv) + && (key1->auth_context_id == key2->auth_context_id) + && (key1->transport_salt == key2->transport_salt)); +} + +static guint +dcerpc_auth_context_hash(gconstpointer k) +{ + const dcerpc_auth_context *key = (const dcerpc_auth_context *)k; + guint hash; + + hash = GPOINTER_TO_UINT(key->conv); + hash += key->auth_context_id; + /* sizeof(guint) might be smaller than sizeof(guint64) */ + hash += (guint)key->transport_salt; + hash += (guint)(key->transport_salt << sizeof(guint)); + + return hash; +} + +/* + * To keep track of callid mappings. Should really use some generic + * conversation support instead. + */ +static wmem_map_t *dcerpc_cn_calls = NULL; +static wmem_map_t *dcerpc_dg_calls = NULL; + +typedef struct _dcerpc_cn_call_key { + conversation_t *conv; + guint32 call_id; + guint64 transport_salt; +} dcerpc_cn_call_key; + +typedef struct _dcerpc_dg_call_key { + conversation_t *conv; + guint32 seqnum; + e_guid_t act_id ; +} dcerpc_dg_call_key; + + +static gint +dcerpc_cn_call_equal(gconstpointer k1, gconstpointer k2) +{ + const dcerpc_cn_call_key *key1 = (const dcerpc_cn_call_key *)k1; + const dcerpc_cn_call_key *key2 = (const dcerpc_cn_call_key *)k2; + return ((key1->conv == key2->conv) + && (key1->call_id == key2->call_id) + && (key1->transport_salt == key2->transport_salt)); +} + +static gint +dcerpc_dg_call_equal(gconstpointer k1, gconstpointer k2) +{ + const dcerpc_dg_call_key *key1 = (const dcerpc_dg_call_key *)k1; + const dcerpc_dg_call_key *key2 = (const dcerpc_dg_call_key *)k2; + return ((key1->conv == key2->conv) + && (key1->seqnum == key2->seqnum) + && ((memcmp(&key1->act_id, &key2->act_id, sizeof (e_guid_t)) == 0))); +} + +static guint +dcerpc_cn_call_hash(gconstpointer k) +{ + const dcerpc_cn_call_key *key = (const dcerpc_cn_call_key *)k; + guint hash; + + hash = GPOINTER_TO_UINT(key->conv); + hash += key->call_id; + /* sizeof(guint) might be smaller than sizeof(guint64) */ + hash += (guint)key->transport_salt; + hash += (guint)(key->transport_salt << sizeof(guint)); + + return hash; +} + +static guint +dcerpc_dg_call_hash(gconstpointer k) +{ + const dcerpc_dg_call_key *key = (const dcerpc_dg_call_key *)k; + return (GPOINTER_TO_UINT(key->conv) + key->seqnum + key->act_id.data1 + + (key->act_id.data2 << 16) + key->act_id.data3 + + (key->act_id.data4[0] << 24) + (key->act_id.data4[1] << 16) + + (key->act_id.data4[2] << 8) + (key->act_id.data4[3] << 0) + + (key->act_id.data4[4] << 24) + (key->act_id.data4[5] << 16) + + (key->act_id.data4[6] << 8) + (key->act_id.data4[7] << 0)); +} + +/* to keep track of matched calls/responses + this one uses the same value struct as calls, but the key is the frame id + and call id; there can be more than one call in a frame. + + XXX - why not just use the same keys as are used for calls? +*/ + +static wmem_map_t *dcerpc_matched = NULL; + +typedef struct _dcerpc_matched_key { + guint32 frame; + guint32 call_id; +} dcerpc_matched_key; + +static gint +dcerpc_matched_equal(gconstpointer k1, gconstpointer k2) +{ + const dcerpc_matched_key *key1 = (const dcerpc_matched_key *)k1; + const dcerpc_matched_key *key2 = (const dcerpc_matched_key *)k2; + return ((key1->frame == key2->frame) + && (key1->call_id == key2->call_id)); +} + +static guint +dcerpc_matched_hash(gconstpointer k) +{ + const dcerpc_matched_key *key = (const dcerpc_matched_key *)k; + return key->frame; +} + +static gboolean +uuid_equal(e_guid_t *uuid1, e_guid_t *uuid2) +{ + if( (uuid1->data1 != uuid2->data1) + ||(uuid1->data2 != uuid2->data2) + ||(uuid1->data3 != uuid2->data3) + ||(uuid1->data4[0] != uuid2->data4[0]) + ||(uuid1->data4[1] != uuid2->data4[1]) + ||(uuid1->data4[2] != uuid2->data4[2]) + ||(uuid1->data4[3] != uuid2->data4[3]) + ||(uuid1->data4[4] != uuid2->data4[4]) + ||(uuid1->data4[5] != uuid2->data4[5]) + ||(uuid1->data4[6] != uuid2->data4[6]) + ||(uuid1->data4[7] != uuid2->data4[7]) ){ + return FALSE; + } + return TRUE; +} + +static void +dcerpcstat_init(struct register_srt* srt, GArray* srt_array) +{ + dcerpcstat_tap_data_t* tap_data = (dcerpcstat_tap_data_t*)get_srt_table_param_data(srt); + srt_stat_table *dcerpc_srt_table; + int i, hf_opnum; + dcerpc_sub_dissector *procs; + + DISSECTOR_ASSERT(tap_data); + + hf_opnum = dcerpc_get_proto_hf_opnum(&tap_data->uuid, tap_data->ver); + procs = dcerpc_get_proto_sub_dissector(&tap_data->uuid, tap_data->ver); + + if(hf_opnum != -1){ + dcerpc_srt_table = init_srt_table(tap_data->prog, NULL, srt_array, tap_data->num_procedures, NULL, proto_registrar_get_nth(hf_opnum)->abbrev, tap_data); + } else { + dcerpc_srt_table = init_srt_table(tap_data->prog, NULL, srt_array, tap_data->num_procedures, NULL, NULL, tap_data); + } + + for(i=0;i<tap_data->num_procedures;i++){ + int j; + const char *proc_name; + + proc_name = "unknown"; + for(j=0;procs[j].name;j++) + { + if (procs[j].num == i) + { + proc_name = procs[j].name; + } + } + + init_srt_table_row(dcerpc_srt_table, i, proc_name); + } +} + +static tap_packet_status +dcerpcstat_packet(void *pss, packet_info *pinfo, epan_dissect_t *edt _U_, const void *prv, tap_flags_t flags _U_) +{ + guint i = 0; + srt_stat_table *dcerpc_srt_table; + srt_data_t *data = (srt_data_t *)pss; + const dcerpc_info *ri = (const dcerpc_info *)prv; + dcerpcstat_tap_data_t* tap_data; + + dcerpc_srt_table = g_array_index(data->srt_array, srt_stat_table*, i); + tap_data = (dcerpcstat_tap_data_t*)dcerpc_srt_table->table_specific_data; + + if(!ri->call_data){ + return TAP_PACKET_DONT_REDRAW; + } + if(!ri->call_data->req_frame){ + /* we have not seen the request so we don't know the delta*/ + return TAP_PACKET_DONT_REDRAW; + } + if(ri->call_data->opnum >= tap_data->num_procedures){ + /* don't handle this since it's outside of known table */ + return TAP_PACKET_DONT_REDRAW; + } + + /* we are only interested in reply packets */ + if(ri->ptype != PDU_RESP){ + return TAP_PACKET_DONT_REDRAW; + } + + /* we are only interested in certain program/versions */ + if( (!uuid_equal( (&ri->call_data->uuid), (&tap_data->uuid))) + ||(ri->call_data->ver != tap_data->ver)){ + return TAP_PACKET_DONT_REDRAW; + } + + add_srt_table_data(dcerpc_srt_table, ri->call_data->opnum, &ri->call_data->req_time, pinfo); + + return TAP_PACKET_REDRAW; +} + +static guint +dcerpcstat_param(register_srt_t* srt, const char* opt_arg, char** err) +{ + int pos = 0; + guint32 i, max_procs; + dcerpcstat_tap_data_t* tap_data; + guint d1,d2,d3,d40,d41,d42,d43,d44,d45,d46,d47; + int major, minor; + guint16 ver; + dcerpc_sub_dissector *procs; + + if (sscanf(opt_arg, ",%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x,%d.%d%n", + &d1,&d2,&d3,&d40,&d41,&d42,&d43,&d44,&d45,&d46,&d47,&major,&minor,&pos) == 13) + { + if ((major < 0) || (major > 65535)) { + *err = ws_strdup_printf("dcerpcstat_init() Major version number %d is invalid - must be positive and <= 65535", major); + return pos; + } + if ((minor < 0) || (minor > 65535)) { + *err = ws_strdup_printf("dcerpcstat_init() Minor version number %d is invalid - must be positive and <= 65535", minor); + return pos; + } + ver = major; + + tap_data = g_new0(dcerpcstat_tap_data_t, 1); + + tap_data->uuid.data1 = d1; + tap_data->uuid.data2 = d2; + tap_data->uuid.data3 = d3; + tap_data->uuid.data4[0] = d40; + tap_data->uuid.data4[1] = d41; + tap_data->uuid.data4[2] = d42; + tap_data->uuid.data4[3] = d43; + tap_data->uuid.data4[4] = d44; + tap_data->uuid.data4[5] = d45; + tap_data->uuid.data4[6] = d46; + tap_data->uuid.data4[7] = d47; + + procs = dcerpc_get_proto_sub_dissector(&tap_data->uuid, ver); + tap_data->prog = dcerpc_get_proto_name(&tap_data->uuid, ver); + tap_data->ver = ver; + + for(i=0,max_procs=0;procs[i].name;i++) + { + if(procs[i].num>max_procs) + { + max_procs = procs[i].num; + } + } + tap_data->num_procedures = max_procs+1; + + set_srt_table_param_data(srt, tap_data); + } + else + { + *err = ws_strdup_printf("<uuid>,<major version>.<minor version>[,<filter>]"); + } + + return pos; +} + + +/* + * Utility functions. Modeled after packet-rpc.c + */ + +int +dissect_dcerpc_char(tvbuff_t *tvb, gint offset, packet_info *pinfo _U_, + proto_tree *tree, guint8 *drep, + int hfindex, guint8 *pdata) +{ + guint8 data; + + /* + * XXX - fix to handle EBCDIC if we ever support EBCDIC FT_CHAR. + */ + data = tvb_get_guint8(tvb, offset); + if (hfindex != -1) { + proto_tree_add_item(tree, hfindex, tvb, offset, 1, ENC_ASCII|DREP_ENC_INTEGER(drep)); + } + if (pdata) + *pdata = data; + tvb_ensure_bytes_exist(tvb, offset, 1); + return offset + 1; +} + +int +dissect_dcerpc_uint8(tvbuff_t *tvb, gint offset, packet_info *pinfo _U_, + proto_tree *tree, guint8 *drep, + int hfindex, guint8 *pdata) +{ + guint8 data; + + data = tvb_get_guint8(tvb, offset); + if (hfindex != -1) { + proto_tree_add_item(tree, hfindex, tvb, offset, 1, DREP_ENC_INTEGER(drep)); + } + if (pdata) + *pdata = data; + tvb_ensure_bytes_exist(tvb, offset, 1); + return offset + 1; +} + +int +dissect_dcerpc_uint16(tvbuff_t *tvb, gint offset, packet_info *pinfo _U_, + proto_tree *tree, guint8 *drep, + int hfindex, guint16 *pdata) +{ + guint16 data; + + data = ((drep[0] & DREP_LITTLE_ENDIAN) + ? tvb_get_letohs(tvb, offset) + : tvb_get_ntohs(tvb, offset)); + + if (hfindex != -1) { + proto_tree_add_item(tree, hfindex, tvb, offset, 2, DREP_ENC_INTEGER(drep)); + } + if (pdata) + *pdata = data; + tvb_ensure_bytes_exist(tvb, offset, 2); + return offset + 2; +} + +int +dissect_dcerpc_uint32(tvbuff_t *tvb, gint offset, packet_info *pinfo _U_, + proto_tree *tree, guint8 *drep, + int hfindex, guint32 *pdata) +{ + guint32 data; + + data = ((drep[0] & DREP_LITTLE_ENDIAN) + ? tvb_get_letohl(tvb, offset) + : tvb_get_ntohl(tvb, offset)); + + if (hfindex != -1) { + proto_tree_add_item(tree, hfindex, tvb, offset, 4, DREP_ENC_INTEGER(drep)); + } + if (pdata) + *pdata = data; + tvb_ensure_bytes_exist(tvb, offset, 4); + return offset+4; +} + +/* handles 32 bit unix time_t */ +int +dissect_dcerpc_time_t(tvbuff_t *tvb, gint offset, packet_info *pinfo _U_, + proto_tree *tree, guint8 *drep, + int hfindex, guint32 *pdata) +{ + guint32 data; + nstime_t tv; + + data = ((drep[0] & DREP_LITTLE_ENDIAN) + ? tvb_get_letohl(tvb, offset) + : tvb_get_ntohl(tvb, offset)); + + tv.secs = data; + tv.nsecs = 0; + if (hfindex != -1) { + if (data == 0xffffffff) { + /* special case, no time specified */ + proto_tree_add_time_format_value(tree, hfindex, tvb, offset, 4, &tv, "No time specified"); + } else { + proto_tree_add_time(tree, hfindex, tvb, offset, 4, &tv); + } + } + if (pdata) + *pdata = data; + + tvb_ensure_bytes_exist(tvb, offset, 4); + return offset+4; +} + +int +dissect_dcerpc_uint64(tvbuff_t *tvb, gint offset, packet_info *pinfo _U_, + proto_tree *tree, dcerpc_info *di, guint8 *drep, + int hfindex, guint64 *pdata) +{ + guint64 data; + + data = ((drep[0] & DREP_LITTLE_ENDIAN) + ? tvb_get_letoh64(tvb, offset) + : tvb_get_ntoh64(tvb, offset)); + + if (hfindex != -1) { + header_field_info *hfinfo; + + /* This might be a field that is either 32bit, in NDR or + 64 bits in NDR64. So we must be careful and call the right + helper here + */ + hfinfo = proto_registrar_get_nth(hfindex); + + switch (hfinfo->type) { + case FT_UINT64: + proto_tree_add_uint64(tree, hfindex, tvb, offset, 8, data); + break; + case FT_INT64: + proto_tree_add_int64(tree, hfindex, tvb, offset, 8, data); + break; + default: + /* The value is truncated to 32bits. 64bit values have only been + seen on fuzz-tested files */ + DISSECTOR_ASSERT((di->call_data->flags & DCERPC_IS_NDR64) || (data <= G_MAXUINT32)); + proto_tree_add_uint(tree, hfindex, tvb, offset, 8, (guint32)data); + } + } + if (pdata) + *pdata = data; + tvb_ensure_bytes_exist(tvb, offset, 8); + return offset+8; +} + + +int +dissect_dcerpc_float(tvbuff_t *tvb, gint offset, packet_info *pinfo, + proto_tree *tree, guint8 *drep, + int hfindex, gfloat *pdata) +{ + gfloat data; + + + switch (drep[1]) { + case(DCE_RPC_DREP_FP_IEEE): + data = ((drep[0] & DREP_LITTLE_ENDIAN) + ? tvb_get_letohieee_float(tvb, offset) + : tvb_get_ntohieee_float(tvb, offset)); + if (tree && hfindex != -1) { + proto_tree_add_float(tree, hfindex, tvb, offset, 4, data); + } + break; + case(DCE_RPC_DREP_FP_VAX): /* (fall trough) */ + case(DCE_RPC_DREP_FP_CRAY): /* (fall trough) */ + case(DCE_RPC_DREP_FP_IBM): /* (fall trough) */ + default: + /* ToBeDone: non IEEE floating formats */ + /* Set data to a negative infinity value */ + data = -G_MAXFLOAT; + proto_tree_add_expert_format(tree, pinfo, &ei_dcerpc_not_implemented, tvb, offset, 4, + "DCE RPC: dissection of non IEEE floating formats currently not implemented (drep=%u)!", + drep[1]); + } + if (pdata) + *pdata = data; + tvb_ensure_bytes_exist(tvb, offset, 4); + return offset + 4; +} + + +int +dissect_dcerpc_double(tvbuff_t *tvb, gint offset, packet_info *pinfo _U_, + proto_tree *tree, guint8 *drep, + int hfindex, gdouble *pdata) +{ + gdouble data; + + + switch (drep[1]) { + case(DCE_RPC_DREP_FP_IEEE): + data = ((drep[0] & DREP_LITTLE_ENDIAN) + ? tvb_get_letohieee_double(tvb, offset) + : tvb_get_ntohieee_double(tvb, offset)); + if (tree && hfindex != -1) { + proto_tree_add_double(tree, hfindex, tvb, offset, 8, data); + } + break; + case(DCE_RPC_DREP_FP_VAX): /* (fall trough) */ + case(DCE_RPC_DREP_FP_CRAY): /* (fall trough) */ + case(DCE_RPC_DREP_FP_IBM): /* (fall trough) */ + default: + /* ToBeDone: non IEEE double formats */ + /* Set data to a negative infinity value */ + data = -G_MAXDOUBLE; + proto_tree_add_expert_format(tree, pinfo, &ei_dcerpc_not_implemented, tvb, offset, 8, + "DCE RPC: dissection of non IEEE double formats currently not implemented (drep=%u)!", + drep[1]); + } + if (pdata) + *pdata = data; + tvb_ensure_bytes_exist(tvb, offset, 8); + return offset + 8; +} + + +int +dissect_dcerpc_uuid_t(tvbuff_t *tvb, gint offset, packet_info *pinfo _U_, + proto_tree *tree, guint8 *drep, + int hfindex, e_guid_t *pdata) +{ + e_guid_t uuid; + + + if (drep[0] & DREP_LITTLE_ENDIAN) { + tvb_get_letohguid(tvb, offset, (e_guid_t *) &uuid); + } else { + tvb_get_ntohguid(tvb, offset, (e_guid_t *) &uuid); + } + if (tree && hfindex != -1) { + proto_tree_add_guid(tree, hfindex, tvb, offset, 16, (e_guid_t *) &uuid); + } + if (pdata) { + *pdata = uuid; + } + return offset + 16; +} + + +/* + * a couple simpler things + */ +guint16 +dcerpc_tvb_get_ntohs(tvbuff_t *tvb, gint offset, guint8 *drep) +{ + if (drep[0] & DREP_LITTLE_ENDIAN) { + return tvb_get_letohs(tvb, offset); + } else { + return tvb_get_ntohs(tvb, offset); + } +} + +guint32 +dcerpc_tvb_get_ntohl(tvbuff_t *tvb, gint offset, guint8 *drep) +{ + if (drep[0] & DREP_LITTLE_ENDIAN) { + return tvb_get_letohl(tvb, offset); + } else { + return tvb_get_ntohl(tvb, offset); + } +} + +void +dcerpc_tvb_get_uuid(tvbuff_t *tvb, gint offset, guint8 *drep, e_guid_t *uuid) +{ + if (drep[0] & DREP_LITTLE_ENDIAN) { + tvb_get_letohguid(tvb, offset, (e_guid_t *) uuid); + } else { + tvb_get_ntohguid(tvb, offset, (e_guid_t *) uuid); + } +} + + +/* NDR arrays */ +/* function to dissect a unidimensional conformant array */ +static int +dissect_ndr_ucarray_core(tvbuff_t *tvb, gint offset, packet_info *pinfo, + proto_tree *tree, dcerpc_info *di, guint8 *drep, + dcerpc_dissect_fnct_t *fnct_bytes, + dcerpc_dissect_fnct_blk_t *fnct_block) +{ + guint32 i; + int old_offset; + int conformance_size = 4; + + /* ensure that just one pointer is set in the call */ + DISSECTOR_ASSERT((fnct_bytes && !fnct_block) || (!fnct_bytes && fnct_block)); + + if (di->call_data->flags & DCERPC_IS_NDR64) { + conformance_size = 8; + } + + if (di->conformant_run) { + guint64 val; + + /* conformant run, just dissect the max_count header */ + old_offset = offset; + di->conformant_run = 0; + offset = dissect_ndr_uint3264(tvb, offset, pinfo, tree, di, drep, + hf_dcerpc_array_max_count, &val); + di->array_max_count = (gint32)val; + di->array_max_count_offset = offset-conformance_size; + di->conformant_run = 1; + di->conformant_eaten = offset-old_offset; + } else { + /* we don't remember where in the bytestream this field was */ + proto_tree_add_uint(tree, hf_dcerpc_array_max_count, tvb, di->array_max_count_offset, conformance_size, di->array_max_count); + + /* real run, dissect the elements */ + if (fnct_block) { + offset = (*fnct_block)(tvb, offset, di->array_max_count, + pinfo, tree, di, drep); + } else { + for (i=0 ;i<di->array_max_count; i++) { + offset = (*fnct_bytes)(tvb, offset, pinfo, tree, di, drep); + } + } + } + + return offset; +} + +int +dissect_ndr_ucarray_block(tvbuff_t *tvb, gint offset, packet_info *pinfo, + proto_tree *tree, dcerpc_info *di, guint8 *drep, + dcerpc_dissect_fnct_blk_t *fnct) +{ + return dissect_ndr_ucarray_core(tvb, offset, pinfo, tree, di, drep, NULL, fnct); +} + +int +dissect_ndr_ucarray(tvbuff_t *tvb, gint offset, packet_info *pinfo, + proto_tree *tree, dcerpc_info *di, guint8 *drep, + dcerpc_dissect_fnct_t *fnct) +{ + return dissect_ndr_ucarray_core(tvb, offset, pinfo, tree, di, drep, fnct, NULL); +} + +/* function to dissect a unidimensional conformant and varying array + * depending on the dissection function passed as a parameter, + * content of the array will be dissected as a block or byte by byte + */ +static int +dissect_ndr_ucvarray_core(tvbuff_t *tvb, gint offset, packet_info *pinfo, + proto_tree *tree, dcerpc_info *di, guint8 *drep, + dcerpc_dissect_fnct_t *fnct_bytes, + dcerpc_dissect_fnct_blk_t *fnct_block) +{ + guint32 i; + int old_offset; + int conformance_size = 4; + + if (di->call_data->flags & DCERPC_IS_NDR64) { + conformance_size = 8; + } + + if (di->conformant_run) { + guint64 val; + + /* conformant run, just dissect the max_count header */ + old_offset = offset; + di->conformant_run = 0; + offset = dissect_ndr_uint3264(tvb, offset, pinfo, tree, di, drep, + hf_dcerpc_array_max_count, &val); + DISSECTOR_ASSERT(val <= G_MAXUINT32); + di->array_max_count = (guint32)val; + di->array_max_count_offset = offset-conformance_size; + offset = dissect_ndr_uint3264(tvb, offset, pinfo, tree, di, drep, + hf_dcerpc_array_offset, &val); + DISSECTOR_ASSERT(val <= G_MAXUINT32); + di->array_offset = (guint32)val; + di->array_offset_offset = offset-conformance_size; + offset = dissect_ndr_uint3264(tvb, offset, pinfo, tree, di, drep, + hf_dcerpc_array_actual_count, &val); + DISSECTOR_ASSERT(val <= G_MAXUINT32); + di->array_actual_count = (guint32)val; + di->array_actual_count_offset = offset-conformance_size; + di->conformant_run = 1; + di->conformant_eaten = offset-old_offset; + } else { + /* we don't remember where in the bytestream these fields were */ + proto_tree_add_uint(tree, hf_dcerpc_array_max_count, tvb, di->array_max_count_offset, conformance_size, di->array_max_count); + proto_tree_add_uint(tree, hf_dcerpc_array_offset, tvb, di->array_offset_offset, conformance_size, di->array_offset); + proto_tree_add_uint(tree, hf_dcerpc_array_actual_count, tvb, di->array_actual_count_offset, conformance_size, di->array_actual_count); + + /* real run, dissect the elements */ + if (fnct_block) { + offset = (*fnct_block)(tvb, offset, di->array_actual_count, + pinfo, tree, di, drep); + } else if (fnct_bytes) { + for (i=0 ;i<di->array_actual_count; i++) { + old_offset = offset; + offset = (*fnct_bytes)(tvb, offset, pinfo, tree, di, drep); + /* Make sure we're moving forward */ + if (old_offset >= offset) + break; + } + } + } + + return offset; +} + +int +dissect_ndr_ucvarray_block(tvbuff_t *tvb, gint offset, packet_info *pinfo, + proto_tree *tree, dcerpc_info *di, guint8 *drep, + dcerpc_dissect_fnct_blk_t *fnct) +{ + return dissect_ndr_ucvarray_core(tvb, offset, pinfo, tree, di, drep, NULL, fnct); +} + +int +dissect_ndr_ucvarray(tvbuff_t *tvb, gint offset, packet_info *pinfo, + proto_tree *tree, dcerpc_info *di, guint8 *drep, + dcerpc_dissect_fnct_t *fnct) +{ + return dissect_ndr_ucvarray_core(tvb, offset, pinfo, tree, di, drep, fnct, NULL); +} +/* function to dissect a unidimensional varying array */ +int +dissect_ndr_uvarray(tvbuff_t *tvb, gint offset, packet_info *pinfo, + proto_tree *tree, dcerpc_info *di, guint8 *drep, + dcerpc_dissect_fnct_t *fnct) +{ + guint32 i; + int old_offset; + int conformance_size = 4; + + if (di->call_data->flags & DCERPC_IS_NDR64) { + conformance_size = 8; + } + + if (di->conformant_run) { + guint64 val; + + /* conformant run, just dissect the max_count header */ + old_offset = offset; + di->conformant_run = 0; + offset = dissect_ndr_uint3264(tvb, offset, pinfo, tree, di, drep, + hf_dcerpc_array_offset, &val); + DISSECTOR_ASSERT(val <= G_MAXUINT32); + di->array_offset = (guint32)val; + di->array_offset_offset = offset-conformance_size; + offset = dissect_ndr_uint3264(tvb, offset, pinfo, tree, di, drep, + hf_dcerpc_array_actual_count, &val); + DISSECTOR_ASSERT(val <= G_MAXUINT32); + di->array_actual_count = (guint32)val; + di->array_actual_count_offset = offset-conformance_size; + di->conformant_run = 1; + di->conformant_eaten = offset-old_offset; + } else { + /* we don't remember where in the bytestream these fields were */ + proto_tree_add_uint(tree, hf_dcerpc_array_offset, tvb, di->array_offset_offset, conformance_size, di->array_offset); + proto_tree_add_uint(tree, hf_dcerpc_array_actual_count, tvb, di->array_actual_count_offset, conformance_size, di->array_actual_count); + + /* real run, dissect the elements */ + for (i=0; i<di->array_actual_count; i++) { + offset = (*fnct)(tvb, offset, pinfo, tree, di, drep); + } + } + + return offset; +} + +/* Dissect an string of bytes. This corresponds to + IDL of the form '[string] byte *foo'. + + It can also be used for a conformant varying array of bytes if + the contents of the array should be shown as a big blob, rather + than showing each byte as an individual element. + + XXX - which of those is really the IDL type for, for example, + the encrypted data in some MAPI packets? (Microsoft hasn't + released that IDL.) + + XXX - does this need to do all the conformant array stuff that + "dissect_ndr_ucvarray()" does? These are presumably for strings + that are conformant and varying - they're stored like conformant + varying arrays of bytes. */ +int +dissect_ndr_byte_array(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *tree, dcerpc_info *di, guint8 *drep) +{ + guint64 len; + + if (di->conformant_run) { + /* just a run to handle conformant arrays, no scalars to dissect */ + return offset; + } + + /* NDR array header */ + + offset = dissect_ndr_uint3264(tvb, offset, pinfo, tree, di, drep, + hf_dcerpc_array_max_count, NULL); + + offset = dissect_ndr_uint3264(tvb, offset, pinfo, tree, di, drep, + hf_dcerpc_array_offset, NULL); + + offset = dissect_ndr_uint3264(tvb, offset, pinfo, tree, di, drep, + hf_dcerpc_array_actual_count, &len); + + DISSECTOR_ASSERT(len <= G_MAXUINT32); + if (len) { + proto_tree_add_item(tree, di->hf_index, tvb, offset, (guint32)len, + ENC_NA); + } + + offset += (guint32)len; + + return offset; +} + +/* For dissecting arrays that are to be interpreted as strings. */ + +/* Dissect an NDR conformant varying string of elements. + The length of each element is given by the 'size_is' parameter; + the elements are assumed to be characters or wide characters. + + XXX - does this need to do all the conformant array stuff that + "dissect_ndr_ucvarray()" does? */ +int +dissect_ndr_cvstring(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *tree, dcerpc_info *di, guint8 *drep, int size_is, + int hfindex, gboolean add_subtree, char **data) +{ + header_field_info *hfinfo; + proto_item *string_item; + proto_tree *string_tree; + guint64 len; + guint32 buffer_len; + char *s; + + /* Make sure this really is a string field. */ + hfinfo = proto_registrar_get_nth(hfindex); + DISSECTOR_ASSERT_FIELD_TYPE(hfinfo, FT_STRING); + + if (di->conformant_run) { + /* just a run to handle conformant arrays, no scalars to dissect */ + return offset; + } + + if (add_subtree) { + string_tree = proto_tree_add_subtree(tree, tvb, offset, -1, ett_dcerpc_string, &string_item, + proto_registrar_get_name(hfindex)); + } else { + string_item = NULL; + string_tree = tree; + } + + /* NDR array header */ + + offset = dissect_ndr_uint3264(tvb, offset, pinfo, string_tree, di, drep, + hf_dcerpc_array_max_count, NULL); + + offset = dissect_ndr_uint3264(tvb, offset, pinfo, string_tree, di, drep, + hf_dcerpc_array_offset, NULL); + + offset = dissect_ndr_uint3264(tvb, offset, pinfo, string_tree, di, drep, + hf_dcerpc_array_actual_count, &len); + + /* The value is truncated to 32bits. 64bit values have only been + seen on fuzztested files */ + buffer_len = size_is * (guint32)len; + + /* Adjust offset */ + if (!di->no_align && (offset % size_is)) + offset += size_is - (offset % size_is); + + /* + * "tvb_get_string_enc()" throws an exception if the entire string + * isn't in the tvbuff. If the length is bogus, this should + * keep us from trying to allocate an immensely large buffer. + * (It won't help if the length is *valid* but immensely large, + * but that's another matter; in any case, that would happen only + * if we had an immensely large tvbuff....) + * + * XXX - so why are we doing tvb_ensure_bytes_exist()? + */ + tvb_ensure_bytes_exist(tvb, offset, buffer_len); + if (size_is == sizeof(guint16)) { + s = tvb_get_string_enc(pinfo->pool, tvb, offset, buffer_len, + ENC_UTF_16|DREP_ENC_INTEGER(drep)); + } else { + /* + * XXX - what if size_is is neither 1 nor 2? + */ + s = tvb_get_string_enc(pinfo->pool, tvb, offset, buffer_len, + DREP_ENC_CHAR(drep)); + } + if (tree && buffer_len) + proto_tree_add_string(string_tree, hfindex, tvb, offset, + buffer_len, s); + + if (string_item != NULL) + proto_item_append_text(string_item, ": %s", s); + + if (data) + *data = s; + + offset += buffer_len; + + proto_item_set_end(string_item, tvb, offset); + + return offset; +} + +int +dissect_ndr_cstring(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *tree, dcerpc_info *di, guint8 *drep, int size_is, + int hfindex, gboolean add_subtree, char **data) +{ + return dissect_ndr_cvstring(tvb, offset, pinfo, tree, di, drep, size_is, hfindex, add_subtree, data); +} + +/* Dissect an conformant varying string of chars. + This corresponds to IDL of the form '[string] char *foo'. + + XXX - at least according to the DCE RPC 1.1 spec, a string has + a null terminator, which isn't necessary as a terminator for + the transfer language (as there's a length), but is presumably + there for the benefit of null-terminated-string languages + such as C. Is this ever used for purely counted strings? + (Not that it matters if it is.) */ +int +dissect_ndr_char_cvstring(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *tree, dcerpc_info *di, guint8 *drep) +{ + return dissect_ndr_cvstring(tvb, offset, pinfo, tree, di, drep, + sizeof(guint8), di->hf_index, + FALSE, NULL); +} + +/* Dissect a conformant varying string of wchars (wide characters). + This corresponds to IDL of the form '[string] wchar *foo' + + XXX - at least according to the DCE RPC 1.1 spec, a string has + a null terminator, which isn't necessary as a terminator for + the transfer language (as there's a length), but is presumably + there for the benefit of null-terminated-string languages + such as C. Is this ever used for purely counted strings? + (Not that it matters if it is.) */ +int +dissect_ndr_wchar_cvstring(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *tree, dcerpc_info *di, guint8 *drep) +{ + return dissect_ndr_cvstring(tvb, offset, pinfo, tree, di, drep, + sizeof(guint16), di->hf_index, + FALSE, NULL); +} + +/* This function is aimed for PIDL usage and dissects a UNIQUE pointer to + * unicode string. + */ +int +PIDL_dissect_cvstring(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree, dcerpc_info *di, guint8 *drep, int chsize, int hfindex, guint32 param) +{ + char *s = NULL; + gint levels = CB_STR_ITEM_LEVELS(param); + + offset = dissect_ndr_cvstring(tvb, offset, pinfo, tree, di, drep, + chsize, hfindex, + FALSE, &s); + + if (!di->conformant_run) { + /* Append string to COL_INFO */ + if (param & PIDL_SET_COL_INFO) { + col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", s); + } + /* Save string to dcv->private_data */ + if ((param & PIDL_STR_SAVE) + && (!pinfo->fd->visited)) { + dcerpc_call_value *dcv = (dcerpc_call_value *)di->call_data; + dcv->private_data = wmem_strdup(wmem_file_scope(), s); + } + /* Append string to upper-level proto_items */ + if ((levels > 0) && tree && s && s[0]) { + proto_item_append_text(tree, ": %s", s); + tree = tree->parent; + levels--; + if (levels > 0) { + proto_item_append_text(tree, ": %s", s); + tree = tree->parent; + levels--; + while (levels > 0) { + proto_item_append_text(tree, " %s", s); + tree = tree->parent; + levels--; + } + } + } + + } + + return offset; +} + +/* Dissect an NDR varying string of elements. + The length of each element is given by the 'size_is' parameter; + the elements are assumed to be characters or wide characters. +*/ +int +dissect_ndr_vstring(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *tree, dcerpc_info *di, guint8 *drep, int size_is, + int hfindex, gboolean add_subtree, char **data) +{ + header_field_info *hfinfo; + proto_item *string_item; + proto_tree *string_tree; + guint64 len; + guint32 buffer_len; + char *s; + + /* Make sure this really is a string field. */ + hfinfo = proto_registrar_get_nth(hfindex); + DISSECTOR_ASSERT_FIELD_TYPE(hfinfo, FT_STRING); + + if (di->conformant_run) { + /* just a run to handle conformant arrays, no scalars to dissect */ + return offset; + } + + if (add_subtree) { + string_tree = proto_tree_add_subtree(tree, tvb, offset, -1, ett_dcerpc_string, &string_item, + proto_registrar_get_name(hfindex)); + } else { + string_item = NULL; + string_tree = tree; + } + + /* NDR array header */ + offset = dissect_ndr_uint3264(tvb, offset, pinfo, string_tree, di, drep, + hf_dcerpc_array_offset, NULL); + + offset = dissect_ndr_uint3264(tvb, offset, pinfo, string_tree, di, drep, + hf_dcerpc_array_actual_count, &len); + + DISSECTOR_ASSERT(len <= G_MAXUINT32); + buffer_len = size_is * (guint32)len; + + /* Adjust offset */ + if (!di->no_align && (offset % size_is)) + offset += size_is - (offset % size_is); + + /* + * "tvb_get_string_enc()" throws an exception if the entire string + * isn't in the tvbuff. If the length is bogus, this should + * keep us from trying to allocate an immensely large buffer. + * (It won't help if the length is *valid* but immensely large, + * but that's another matter; in any case, that would happen only + * if we had an immensely large tvbuff....) + * + * XXX - so why are we doing tvb_ensure_bytes_exist()? + */ + tvb_ensure_bytes_exist(tvb, offset, buffer_len); + if (size_is == sizeof(guint16)) { + s = tvb_get_string_enc(pinfo->pool, tvb, offset, buffer_len, + ENC_UTF_16|DREP_ENC_INTEGER(drep)); + } else { + /* + * XXX - what if size_is is neither 1 nor 2? + */ + s = tvb_get_string_enc(pinfo->pool, tvb, offset, buffer_len, + DREP_ENC_CHAR(drep)); + } + if (tree && buffer_len) + proto_tree_add_string(string_tree, hfindex, tvb, offset, + buffer_len, s); + + if (string_item != NULL) + proto_item_append_text(string_item, ": %s", s); + + if (data) + *data = s; + + offset += buffer_len; + + proto_item_set_end(string_item, tvb, offset); + + return offset; +} + +/* Dissect an varying string of chars. + This corresponds to IDL of the form '[string] char *foo'. + + XXX - at least according to the DCE RPC 1.1 spec, a string has + a null terminator, which isn't necessary as a terminator for + the transfer language (as there's a length), but is presumably + there for the benefit of null-terminated-string languages + such as C. Is this ever used for purely counted strings? + (Not that it matters if it is.) */ +int +dissect_ndr_char_vstring(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *tree, dcerpc_info *di, guint8 *drep) +{ + return dissect_ndr_vstring(tvb, offset, pinfo, tree, di, drep, + sizeof(guint8), di->hf_index, + FALSE, NULL); +} + +/* Dissect a varying string of wchars (wide characters). + This corresponds to IDL of the form '[string] wchar *foo' + + XXX - at least according to the DCE RPC 1.1 spec, a string has + a null terminator, which isn't necessary as a terminator for + the transfer language (as there's a length), but is presumably + there for the benefit of null-terminated-string languages + such as C. Is this ever used for purely counted strings? + (Not that it matters if it is.) */ +int +dissect_ndr_wchar_vstring(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *tree, dcerpc_info *di, guint8 *drep) +{ + return dissect_ndr_vstring(tvb, offset, pinfo, tree, di, drep, + sizeof(guint16), di->hf_index, + FALSE, NULL); +} + +/* ndr pointer handling */ +/* Should we re-read the size of the list ? + * Instead of re-calculating the size everytime, use the stored value unless this + * flag is set which means: re-read the size of the list + */ +static gboolean must_check_size = FALSE; +/* + * List of pointers encountered so far in the current level. Points to an + * element of list_ndr_pointer_list. + */ +static GSList *ndr_pointer_list = NULL; + +static GHashTable *ndr_pointer_hash = NULL; +/* + * List of pointer list, in order to avoid huge performance penalty + * when dealing with list bigger than 100 elements due to the way we + * try to insert in the list. + * We instead maintain a stack of pointer list + * To make it easier to manage we just use a list to materialize the stack + */ +static GSList *list_ndr_pointer_list = NULL; + +/* Boolean controlling whether pointers are top-level or embedded */ +static gboolean pointers_are_top_level = TRUE; + +/* as a kludge, we represent all embedded reference pointers as id == -1 + hoping that his will not collide with any non-ref pointers */ +typedef struct ndr_pointer_data { + guint32 id; + proto_item *item; /* proto_item for pointer */ + proto_tree *tree; /* subtree of above item */ + dcerpc_dissect_fnct_t *fnct; /*if non-NULL, we have not called it yet*/ + int hf_index; + dcerpc_callback_fnct_t *callback; + void *callback_args; +} ndr_pointer_data_t; + +void +init_ndr_pointer_list(dcerpc_info *di) +{ + di->conformant_run = 0; + + while (list_ndr_pointer_list) { + GSList *list = (GSList *)g_slist_nth_data(list_ndr_pointer_list, 0); + list_ndr_pointer_list = g_slist_remove(list_ndr_pointer_list, list); + g_slist_free_full(list, g_free); + } + g_slist_free_full(list_ndr_pointer_list, g_free); + + list_ndr_pointer_list = NULL; + pointers_are_top_level = TRUE; + must_check_size = FALSE; + + ndr_pointer_list = NULL; + if (ndr_pointer_hash) { + g_hash_table_destroy(ndr_pointer_hash); + } + ndr_pointer_hash = g_hash_table_new(g_int_hash, g_int_equal); +} + +int +dissect_deferred_pointers(packet_info *pinfo, tvbuff_t *tvb, int offset, dcerpc_info *di, guint8 *drep) +{ + int found_new_pointer; + int old_offset; + int next_pointer; + unsigned original_depth; + int len; + GSList *current_ndr_pointer_list; + + /* + * pidl has a difficiency of unconditionally emitting calls + * dissect_deferred_pointers() to the generated dissectors. + */ + if (list_ndr_pointer_list == NULL) { + return offset; + } + + /* Probably not necessary, it is supposed to prevent more pointers from + * being added to the list. */ + ndr_pointer_list = NULL; + + next_pointer = 0; + + /* Obtain the current list of pointers at this level. */ + current_ndr_pointer_list = (GSList *)g_slist_last(list_ndr_pointer_list)->data; + original_depth = g_slist_length(list_ndr_pointer_list); + + len = g_slist_length(current_ndr_pointer_list); + do { + int i; + + found_new_pointer = 0; +process_list: + for (i=next_pointer; i<len; i++) { + ndr_pointer_data_t *tnpd = (ndr_pointer_data_t *)g_slist_nth_data(current_ndr_pointer_list, i); + + if (tnpd->fnct) { + GSList *saved_ndr_pointer_list = NULL; + + dcerpc_dissect_fnct_t *fnct; + + next_pointer = i+1; + found_new_pointer = 1; + fnct = tnpd->fnct; + tnpd->fnct = NULL; + di->hf_index = tnpd->hf_index; + /* first a run to handle any conformant + array headers */ + di->conformant_run = 1; + di->conformant_eaten = 0; + old_offset = offset; + saved_ndr_pointer_list = current_ndr_pointer_list; + ndr_pointer_list = NULL; + offset = (*(fnct))(tvb, offset, pinfo, NULL, di, drep); + + DISSECTOR_ASSERT((offset-old_offset) == di->conformant_eaten); + /* This is to check for any bugs in the dissectors. + * + * Basically, the NDR representation will store all + * arrays in two blocks, one block with the dimension + * description, like size, number of elements and such, + * and another block that contains the actual data stored + * in the array. + * If the array is embedded directly inside another, + * encapsulating aggregate type, like a union or struct, + * then these two blocks will be stored at different places + * in the bytestream, with other data between the blocks. + * + * For this reason, all pointers to types (both aggregate + * and scalar, for simplicity no distinction is made) + * will have its dissector called twice. + * The dissector will first be called with conformant_run == 1 + * in which mode the dissector MUST NOT consume any data from + * the tvbuff (i.e. may not dissect anything) except the + * initial control block for arrays. + * The second time the dissector is called, with + * conformant_run == 0, all other data for the type will be + * dissected. + * + * All dissect_ndr_<type> dissectors are already prepared + * for this and knows when it should eat data from the tvb + * and when not to, so implementors of dissectors will + * normally not need to worry about this or even know about + * it. However, if a dissector for an aggregate type calls + * a subdissector from outside packet-dcerpc.c, such as + * the dissector in packet-smb.c for NT Security Descriptors + * as an example, then it is VERY important to encapsulate + * this call to an external subdissector with the appropriate + * test for conformant_run, i.e. it will need something like + * + * dcerpc_info *di (received as function parameter) + * + * if (di->conformant_run) { + * return offset; + * } + * + * to make sure it makes the right thing. + * This assert will signal when someone has forgotten to + * make the dissector aware of this requirement. + */ + + /* now we dissect the actual pointer */ + di->conformant_run = 0; + old_offset = offset; + offset = (*(fnct))(tvb, offset, pinfo, tnpd->tree, di, drep); + if (tnpd->callback) + tnpd->callback(pinfo, tnpd->tree, tnpd->item, di, tvb, old_offset, offset, tnpd->callback_args); + proto_item_set_len(tnpd->item, offset - old_offset); + if (ndr_pointer_list) { + /* We found some pointers to dissect, descend into it. */ + next_pointer = 0; + len = g_slist_length(ndr_pointer_list); + current_ndr_pointer_list = ndr_pointer_list; + ndr_pointer_list = NULL; + goto process_list; /* Process the new current_ndr_pointer_list */ + } else { + current_ndr_pointer_list = saved_ndr_pointer_list; + } + } + /* If we found the end of the list, but add_pointer_to_list extended + * it, then be sure to handle those extra elements. */ + if (i == (len - 1) && (must_check_size == TRUE)) { + len = g_slist_length(ndr_pointer_list); + must_check_size = FALSE; + } + } + + /* We reached the end of one level, go to the level bellow if possible + * reset list a level n + */ + if ((i >= (len - 1)) && (g_slist_length(list_ndr_pointer_list) > original_depth)) { + GSList *list; + /* Remove existing list */ + g_slist_free_full(current_ndr_pointer_list, g_free); + list = (GSList *)g_slist_last(list_ndr_pointer_list)->data; + list_ndr_pointer_list = g_slist_remove(list_ndr_pointer_list, list); + + /* Rewind on the lower level, in theory it's not too great because we + * will one more time iterate on pointers already done + * In practice it shouldn't be that bad ! + */ + next_pointer = 0; + /* Move to the next list of pointers. */ + current_ndr_pointer_list = (GSList *)g_slist_last(list_ndr_pointer_list)->data; + len = g_slist_length(current_ndr_pointer_list); + found_new_pointer = 1; + } + + } while (found_new_pointer); + DISSECTOR_ASSERT(original_depth == g_slist_length(list_ndr_pointer_list)); + + g_slist_free_full(ndr_pointer_list, g_free); + /* Restore the previous list of pointers. */ + ndr_pointer_list = (GSList *)g_slist_last(list_ndr_pointer_list)->data; + + return offset; +} + +static int +find_pointer_index(guint32 id) +{ + guint *p = (guint*) g_hash_table_lookup(ndr_pointer_hash, &id); + + return (p != NULL); +} + +static void +add_pointer_to_list(packet_info *pinfo, proto_tree *tree, proto_item *item, + dcerpc_info *di, dcerpc_dissect_fnct_t *fnct, guint32 id, int hf_index, + dcerpc_callback_fnct_t *callback, void *callback_args) +{ + ndr_pointer_data_t *npd; + guint *p_id; + + /* check if this pointer is valid */ + if (id != 0xffffffff) { + dcerpc_call_value *value; + + value = di->call_data; + + if (di->ptype == PDU_REQ) { + if (!(pinfo->fd->visited)) { + if (id > value->max_ptr) { + value->max_ptr = id; + } + } + } else { + /* if we haven't seen the request bail out since we can't + know whether this is the first non-NULL instance + or not */ + if (value->req_frame == 0) { + /* XXX THROW EXCEPTION */ + } + + /* We saw this one in the request frame, nothing to + dissect later */ + if (id <= value->max_ptr) { + return; + } + } + } + + npd = g_new(ndr_pointer_data_t, 1); + npd->id = id; + npd->tree = tree; + npd->item = item; + npd->fnct = fnct; + npd->hf_index = hf_index; + npd->callback = callback; + npd->callback_args = callback_args; + p_id = wmem_new(wmem_file_scope(), guint); + *p_id = id; + + /* Update the list of pointers for use by dissect_deferred_pointers. If this + * is the first pointer, create a list and add it to the stack. */ + if (!ndr_pointer_list) { + ndr_pointer_list = g_slist_append(NULL, npd); + list_ndr_pointer_list = g_slist_append(list_ndr_pointer_list, ndr_pointer_list); + } else { + ndr_pointer_list = g_slist_append(ndr_pointer_list, npd); + } + g_hash_table_insert(ndr_pointer_hash, p_id, p_id); + must_check_size = TRUE; +} + + +/* This function dissects an NDR pointer and stores the callback for later + * deferred dissection. + * + * fnct is the callback function for when we have reached this object in + * the bytestream. + * + * type is what type of pointer. + * + * this is text is what text we should put in any created tree node. + * + * hf_index is what hf value we want to pass to the callback function when + * it is called, the callback can later pick this one up from di->hf_index. + * + * callback is executed after the pointer has been dereferenced. + * + * callback_args is passed as an argument to the callback function + * + * See packet-dcerpc-samr.c for examples + */ +int +dissect_ndr_pointer_cb(tvbuff_t *tvb, gint offset, packet_info *pinfo, + proto_tree *tree, dcerpc_info *di, guint8 *drep, dcerpc_dissect_fnct_t *fnct, + int type, const char *text, int hf_index, + dcerpc_callback_fnct_t *callback, void *callback_args) +{ + proto_tree *tr = NULL; + gint start_offset = offset; + int pointer_size = 4; + + if (di->conformant_run) { + /* this call was only for dissecting the header for any + embedded conformant array. we will not parse any + pointers in this mode. + */ + return offset; + } + if (di->call_data->flags & DCERPC_IS_NDR64) { + pointer_size = 8; + } + + + /*TOP LEVEL REFERENCE POINTER*/ + if ( pointers_are_top_level + && (type == NDR_POINTER_REF) ) { + proto_item *item; + + /* we must find out a nice way to do the length here */ + tr = proto_tree_add_subtree(tree, tvb, offset, 0, + ett_dcerpc_pointer_data, &item, text); + + add_pointer_to_list(pinfo, tr, item, di, fnct, 0xffffffff, + hf_index, callback, callback_args); + goto after_ref_id; + } + + /*TOP LEVEL FULL POINTER*/ + if ( pointers_are_top_level + && (type == NDR_POINTER_PTR) ) { + int found; + guint64 id; + proto_item *item; + + /* get the referent id */ + offset = dissect_ndr_uint3264(tvb, offset, pinfo, NULL, di, drep, -1, &id); + + /* we got a NULL pointer */ + if (id == 0) { + proto_tree_add_bytes_format_value(tree, hf_dcerpc_null_pointer, tvb, offset-pointer_size, + pointer_size, NULL, "%s", text); + goto after_ref_id; + } + + /* see if we have seen this pointer before + The value is truncated to 32bits. 64bit values have only been + seen on fuzz-tested files */ + found = find_pointer_index((guint32)id); + + /* we have seen this pointer before */ + if (found) { + proto_tree_add_string(tree, hf_dcerpc_duplicate_ptr, tvb, offset-pointer_size, pointer_size, text); + goto after_ref_id; + } + + /* new pointer */ + tr = proto_tree_add_subtree(tree, tvb, offset-pointer_size, + pointer_size, ett_dcerpc_pointer_data, &item, text); + if (di->call_data->flags & DCERPC_IS_NDR64) { + proto_tree_add_uint64(tr, hf_dcerpc_referent_id64, tvb, + offset-pointer_size, pointer_size, id); + } else { + proto_tree_add_uint(tr, hf_dcerpc_referent_id32, tvb, + offset-pointer_size, pointer_size, (guint32)id); + } + add_pointer_to_list(pinfo, tr, item, di, fnct, (guint32)id, hf_index, + callback, callback_args); + goto after_ref_id; + } + /*TOP LEVEL UNIQUE POINTER*/ + if ( pointers_are_top_level + && (type == NDR_POINTER_UNIQUE) ) { + guint64 id; + proto_item *item; + + /* get the referent id */ + offset = dissect_ndr_uint3264(tvb, offset, pinfo, NULL, di, drep, -1, &id); + + /* we got a NULL pointer */ + if (id == 0) { + proto_tree_add_bytes_format_value(tree, hf_dcerpc_null_pointer, tvb, offset-pointer_size, + pointer_size, NULL, "%s",text); + goto after_ref_id; + } + + /* new pointer */ + tr = proto_tree_add_subtree(tree, tvb, offset-pointer_size, + pointer_size, + ett_dcerpc_pointer_data, &item, text); + if (di->call_data->flags & DCERPC_IS_NDR64) { + proto_tree_add_uint64(tr, hf_dcerpc_referent_id64, tvb, + offset-pointer_size, pointer_size, id); + } else { + proto_tree_add_uint(tr, hf_dcerpc_referent_id32, tvb, + offset-pointer_size, pointer_size, (guint32)id); + } + add_pointer_to_list(pinfo, tr, item, di, fnct, 0xffffffff, + hf_index, callback, callback_args); + goto after_ref_id; + } + + /*EMBEDDED REFERENCE POINTER*/ + if ( (!pointers_are_top_level) + && (type == NDR_POINTER_REF) ) { + guint64 id; + proto_item *item; + + /* get the referent id */ + offset = dissect_ndr_uint3264(tvb, offset, pinfo, NULL, di, drep, -1, &id); + + /* new pointer */ + tr = proto_tree_add_subtree(tree, tvb, offset-pointer_size, + pointer_size, + ett_dcerpc_pointer_data,&item,text); + if (di->call_data->flags & DCERPC_IS_NDR64) { + proto_tree_add_uint64(tr, hf_dcerpc_referent_id64, tvb, + offset-pointer_size, pointer_size, id); + } else { + proto_tree_add_uint(tr, hf_dcerpc_referent_id32, tvb, + offset-pointer_size, pointer_size, (guint32)id); + } + add_pointer_to_list(pinfo, tr, item, di, fnct, 0xffffffff, + hf_index, callback, callback_args); + goto after_ref_id; + } + + /*EMBEDDED UNIQUE POINTER*/ + if ( (!pointers_are_top_level) + && (type == NDR_POINTER_UNIQUE) ) { + guint64 id; + proto_item *item; + + /* get the referent id */ + offset = dissect_ndr_uint3264(tvb, offset, pinfo, NULL, di, drep, -1, &id); + + /* we got a NULL pointer */ + if (id == 0) { + proto_tree_add_bytes_format_value(tree, hf_dcerpc_null_pointer, tvb, offset-pointer_size, + pointer_size, NULL, "%s",text); + goto after_ref_id; + } + + /* new pointer */ + tr = proto_tree_add_subtree(tree, tvb, offset-pointer_size, + pointer_size, + ett_dcerpc_pointer_data,&item,text); + if (di->call_data->flags & DCERPC_IS_NDR64) { + proto_tree_add_uint64(tr, hf_dcerpc_referent_id64, tvb, + offset-pointer_size, pointer_size, id); + } else { + proto_tree_add_uint(tr, hf_dcerpc_referent_id32, tvb, + offset-pointer_size, pointer_size, (guint32)id); + } + add_pointer_to_list(pinfo, tr, item, di, fnct, 0xffffffff, + hf_index, callback, callback_args); + goto after_ref_id; + } + + /*EMBEDDED FULL POINTER*/ + if ( (!pointers_are_top_level) + && (type == NDR_POINTER_PTR) ) { + int found; + guint64 id; + proto_item *item; + + /* get the referent id */ + offset = dissect_ndr_uint3264(tvb, offset, pinfo, NULL, di, drep, -1, &id); + + /* we got a NULL pointer */ + if (id == 0) { + proto_tree_add_bytes_format_value(tree, hf_dcerpc_null_pointer, tvb, offset-pointer_size, + pointer_size, NULL, "%s",text); + goto after_ref_id; + } + + /* see if we have seen this pointer before + The value is truncated to 32bits. 64bit values have only been + seen on fuzztested files */ + found = find_pointer_index((guint32)id); + + /* we have seen this pointer before */ + if (found) { + proto_tree_add_string(tree, hf_dcerpc_duplicate_ptr, tvb, offset-pointer_size, pointer_size, text); + goto after_ref_id; + } + + /* new pointer */ + tr = proto_tree_add_subtree(tree, tvb, offset-pointer_size, + pointer_size, + ett_dcerpc_pointer_data, &item, text); + if (di->call_data->flags & DCERPC_IS_NDR64) { + proto_tree_add_uint64(tr, hf_dcerpc_referent_id64, tvb, + offset-pointer_size, pointer_size, id); + } else { + proto_tree_add_uint(tr, hf_dcerpc_referent_id32, tvb, + offset-pointer_size, pointer_size, (guint32)id); + } + add_pointer_to_list(pinfo, tr, item, di, fnct, (guint32)id, hf_index, + callback, callback_args); + goto after_ref_id; + } + + +after_ref_id: + /* After each top level pointer we have dissected we have to + dissect all deferrals before we move on to the next top level + argument */ + if (pointers_are_top_level == TRUE) { + pointers_are_top_level = FALSE; + offset = dissect_deferred_pointers(pinfo, tvb, offset, di, drep); + pointers_are_top_level = TRUE; + } + + /* Set the length for the new subtree */ + if (tr) { + proto_item_set_len(tr, offset-start_offset); + } + return offset; +} + +int +dissect_ndr_pointer(tvbuff_t *tvb, gint offset, packet_info *pinfo, + proto_tree *tree, dcerpc_info *di, guint8 *drep, dcerpc_dissect_fnct_t *fnct, + int type, const char *text, int hf_index) +{ + return dissect_ndr_pointer_cb( + tvb, offset, pinfo, tree, di, drep, fnct, type, text, hf_index, + NULL, NULL); +} +int +dissect_ndr_toplevel_pointer(tvbuff_t *tvb, gint offset, packet_info *pinfo, + proto_tree *tree, dcerpc_info *di, guint8 *drep, dcerpc_dissect_fnct_t *fnct, + int type, const char *text, int hf_index) +{ + int ret; + + pointers_are_top_level = TRUE; + ret = dissect_ndr_pointer_cb( + tvb, offset, pinfo, tree, di, drep, fnct, type, text, hf_index, + NULL, NULL); + return ret; +} +int +dissect_ndr_embedded_pointer(tvbuff_t *tvb, gint offset, packet_info *pinfo, + proto_tree *tree, dcerpc_info *di, guint8 *drep, dcerpc_dissect_fnct_t *fnct, + int type, const char *text, int hf_index) +{ + int ret; + + pointers_are_top_level = FALSE; + ret = dissect_ndr_pointer_cb( + tvb, offset, pinfo, tree, di, drep, fnct, type, text, hf_index, + NULL, NULL); + return ret; +} + +static void +dissect_sec_vt_bitmask(proto_tree *tree, tvbuff_t *tvb) +{ + proto_tree_add_bitmask(tree, tvb, 0, + hf_dcerpc_sec_vt_bitmask, + ett_dcerpc_sec_vt_bitmask, + sec_vt_bitmask_fields, + ENC_LITTLE_ENDIAN); +} + +static void +dissect_sec_vt_pcontext(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb) +{ + int offset = 0; + proto_item *ti = NULL; + proto_tree *tr = proto_tree_add_subtree(tree, tvb, offset, -1, + ett_dcerpc_sec_vt_pcontext, + &ti, "pcontext"); + e_guid_t uuid; + const char *uuid_name; + + tvb_get_letohguid(tvb, offset, &uuid); + uuid_name = guids_get_uuid_name(&uuid, pinfo->pool); + if (!uuid_name) { + uuid_name = guid_to_str(pinfo->pool, &uuid); + } + + proto_tree_add_guid_format(tr, hf_dcerpc_sec_vt_pcontext_uuid, tvb, + offset, 16, &uuid, "Abstract Syntax: %s", uuid_name); + offset += 16; + + proto_tree_add_item(tr, hf_dcerpc_sec_vt_pcontext_ver, + tvb, offset, 4, ENC_LITTLE_ENDIAN); + offset += 4; + + tvb_get_letohguid(tvb, offset, &uuid); + uuid_name = guids_get_uuid_name(&uuid, pinfo->pool); + if (!uuid_name) { + uuid_name = guid_to_str(pinfo->pool, &uuid); + } + + proto_tree_add_guid_format(tr, hf_dcerpc_sec_vt_pcontext_uuid, tvb, + offset, 16, &uuid, "Transfer Syntax: %s", uuid_name); + offset += 16; + + proto_tree_add_item(tr, hf_dcerpc_sec_vt_pcontext_ver, + tvb, offset, 4, ENC_LITTLE_ENDIAN); + offset += 4; + + proto_item_set_len(ti, offset); +} + +static void +dissect_sec_vt_header(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb) +{ + int offset = 0; + proto_item *ti = NULL; + proto_tree *tr = proto_tree_add_subtree(tree, tvb, offset, -1, + ett_dcerpc_sec_vt_header, + &ti, "header2"); + guint8 drep[4]; + guint8 ptype = tvb_get_guint8(tvb, offset); + + proto_tree_add_uint(tr, hf_dcerpc_packet_type, tvb, offset, 1, ptype); + offset += 1; + + proto_tree_add_item(tr, hf_dcerpc_reserved, tvb, offset, 1, ENC_NA); + offset += 1; + + proto_tree_add_item(tr, hf_dcerpc_reserved, tvb, offset, 2, ENC_NA); + offset += 2; + + tvb_memcpy(tvb, drep, offset, 4); + proto_tree_add_dcerpc_drep(tr, tvb, offset, drep, 4); + offset += 4; + + offset = dissect_dcerpc_uint32(tvb, offset, pinfo, tr, drep, + hf_dcerpc_cn_call_id, NULL); + + offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tr, drep, + hf_dcerpc_cn_ctx_id, NULL); + + offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tr, drep, + hf_dcerpc_opnum, NULL); + + proto_item_set_len(ti, offset); +} + +static int +dissect_verification_trailer_impl(packet_info *pinfo, tvbuff_t *tvb, int stub_offset, + proto_tree *parent_tree, int *signature_offset) +{ + int remaining = tvb_captured_length_remaining(tvb, stub_offset); + int offset; + gint signature_start; + gint payload_length; + typedef enum { + SEC_VT_COMMAND_BITMASK_1 = 0x0001, + SEC_VT_COMMAND_PCONTEXT = 0x0002, + SEC_VT_COMMAND_HEADER2 = 0x0003, + SEC_VT_COMMAND_END = 0x4000, + SEC_VT_MUST_PROCESS_COMMAND = 0x8000, + SEC_VT_COMMAND_MASK = 0x3fff, + } sec_vt_command; + proto_item *payload_item; + proto_item *item; + proto_tree *tree; + + if (signature_offset != NULL) { + *signature_offset = -1; + } + + /* We need at least signature + the header of one command */ + if (remaining < (int)(sizeof(TRAILER_SIGNATURE) + 4)) { + return -1; + } + + /* We only scan the last 512 bytes for a possible trailer */ + if (remaining > 512) { + offset = remaining - 512; + remaining = 512; + } else { + offset = 0; + } + offset += stub_offset; + + signature_start = tvb_find_tvb(tvb, tvb_trailer_signature, offset); + if (signature_start == -1) { + return -1; + } + payload_length = signature_start - stub_offset; + payload_item = proto_tree_add_item(parent_tree, + hf_dcerpc_payload_stub_data, + tvb, stub_offset, payload_length, ENC_NA); + proto_item_append_text(payload_item, " (%d byte%s)", + payload_length, plurality(payload_length, "", "s")); + + if (signature_offset != NULL) { + *signature_offset = signature_start; + } + remaining -= (signature_start - offset); + offset = signature_start; + + tree = proto_tree_add_subtree(parent_tree, tvb, offset, -1, + ett_dcerpc_verification_trailer, + &item, "Verification Trailer"); + + proto_tree_add_item(tree, hf_dcerpc_sec_vt_signature, + tvb, offset, sizeof(TRAILER_SIGNATURE), ENC_NA); + offset += (int)sizeof(TRAILER_SIGNATURE); + remaining -= (int)sizeof(TRAILER_SIGNATURE); + + while (remaining >= 4) { + sec_vt_command cmd; + guint16 len, len_missalign; + gboolean cmd_end, cmd_must; + proto_item *ti; + proto_tree *tr; + tvbuff_t *cmd_tvb = NULL; + + cmd = (sec_vt_command)tvb_get_letohs(tvb, offset); + len = tvb_get_letohs(tvb, offset + 2); + cmd_end = cmd & SEC_VT_COMMAND_END; + cmd_must = cmd & SEC_VT_MUST_PROCESS_COMMAND; + cmd = (sec_vt_command)(cmd & SEC_VT_COMMAND_MASK); + + tr = proto_tree_add_subtree_format(tree, tvb, offset, 4 + len, + ett_dcerpc_sec_vt_pcontext, + &ti, "Command: %s", + val_to_str(cmd, sec_vt_command_cmd_vals, + "Unknown (0x%04x)")); + + if (cmd_must) { + proto_item_append_text(ti, "!!!"); + } + if (cmd_end) { + proto_item_append_text(ti, ", END"); + } + + proto_tree_add_bitmask(tr, tvb, offset, + hf_dcerpc_sec_vt_command, + ett_dcerpc_sec_vt_command, + sec_vt_command_fields, + ENC_LITTLE_ENDIAN); + offset += 2; + + proto_tree_add_item(tr, hf_dcerpc_sec_vt_command_length, tvb, + offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + cmd_tvb = tvb_new_subset_length(tvb, offset, len); + switch (cmd) { + case SEC_VT_COMMAND_BITMASK_1: + dissect_sec_vt_bitmask(tr, cmd_tvb); + break; + case SEC_VT_COMMAND_PCONTEXT: + dissect_sec_vt_pcontext(pinfo, tr, cmd_tvb); + break; + case SEC_VT_COMMAND_HEADER2: + dissect_sec_vt_header(pinfo, tr, cmd_tvb); + break; + default: + proto_tree_add_item(tr, hf_dcerpc_unknown, cmd_tvb, 0, len, ENC_NA); + break; + } + + offset += len; + remaining -= (4 + len); + + len_missalign = len & 1; + + if (len_missalign) { + int l = 2-len_missalign; + proto_tree_add_item(tr, hf_dcerpc_missalign, tvb, offset, l, ENC_NA); + offset += l; + remaining -= l; + } + + if (cmd_end) { + break; + } + } + + proto_item_set_end(item, tvb, offset); + return offset; +} + +static int +dissect_verification_trailer(packet_info *pinfo, tvbuff_t *tvb, int stub_offset, + proto_tree *parent_tree, int *signature_offset) +{ + volatile int ret = -1; + TRY { + /* + * Even if we found a signature we can't be sure to have a + * valid verification trailer, we're only relatively sure + * if we manage to dissect it completely, otherwise it + * may be part of the real payload. That's why we have + * a try/catch block here. + */ + ret = dissect_verification_trailer_impl(pinfo, tvb, stub_offset, parent_tree, signature_offset); + } CATCH_NONFATAL_ERRORS { + } ENDTRY; + return ret; +} + +static int +dcerpc_try_handoff(packet_info *pinfo, proto_tree *tree, + proto_tree *dcerpc_tree, + tvbuff_t *volatile tvb, gboolean decrypted, + guint8 *drep, dcerpc_info *info, + dcerpc_auth_info *auth_info) +{ + volatile gint offset = 0; + guid_key key; + dcerpc_dissector_data_t dissector_data; + proto_item *hidden_item; + + /* GUID and UUID are same size, but compiler complains about structure "name" differences */ + memcpy(&key.guid, &info->call_data->uuid, sizeof(key.guid)); + key.ver = info->call_data->ver; + + dissector_data.sub_proto = (dcerpc_uuid_value *)g_hash_table_lookup(dcerpc_uuids, &key); + dissector_data.info = info; + dissector_data.decrypted = decrypted; + dissector_data.auth_info = auth_info; + dissector_data.drep = drep; + dissector_data.dcerpc_tree = dcerpc_tree; + + /* Check the dissector table before the hash table. Hopefully the hash table entries can + all be converted to use dissector table */ + if ((dissector_data.sub_proto == NULL) || + (!dissector_try_guid_new(uuid_dissector_table, &key, tvb, pinfo, tree, FALSE, &dissector_data))) { + /* + * We don't have a dissector for this UUID, or the protocol + * for that UUID is disabled. + */ + + hidden_item = proto_tree_add_boolean(dcerpc_tree, hf_dcerpc_unknown_if_id, + tvb, offset, 0, TRUE); + proto_item_set_hidden(hidden_item); + col_append_fstr(pinfo->cinfo, COL_INFO, " %s V%u", + guids_resolve_guid_to_str(&info->call_data->uuid, pinfo->pool), info->call_data->ver); + + show_stub_data(pinfo, tvb, 0, dcerpc_tree, auth_info, !decrypted); + return -1; + } + + tap_queue_packet(dcerpc_tap, pinfo, info); + return 0; +} + +static void +dissect_dcerpc_cn_auth_move(dcerpc_auth_info *auth_info, proto_tree *dcerpc_tree) +{ + if (auth_info->auth_item != NULL) { + proto_item *last_item = proto_tree_add_item(dcerpc_tree, hf_dcerpc_auth_info, + auth_info->auth_tvb, 0, 0, ENC_NA); + if (last_item != NULL) { + proto_item_set_hidden(last_item); + proto_tree_move_item(dcerpc_tree, last_item, auth_info->auth_item); + } + } +} + +static dcerpc_auth_context *find_or_create_dcerpc_auth_context(packet_info *pinfo, + dcerpc_auth_info *auth_info) +{ + dcerpc_auth_context auth_key = { + .conv = find_or_create_conversation(pinfo), + .transport_salt = dcerpc_get_transport_salt(pinfo), + .auth_type = auth_info->auth_type, + .auth_level = auth_info->auth_level, + .auth_context_id = auth_info->auth_context_id, + .first_frame = G_MAXUINT32, + }; + dcerpc_auth_context *auth_value = NULL; + + auth_value = (dcerpc_auth_context *)wmem_map_lookup(dcerpc_auths, &auth_key); + if (auth_value != NULL) { + goto return_value; + } + + auth_value = wmem_new(wmem_file_scope(), dcerpc_auth_context); + if (auth_value == NULL) { + return NULL; + } + + *auth_value = auth_key; + wmem_map_insert(dcerpc_auths, auth_value, auth_value); + +return_value: + if (pinfo->fd->num < auth_value->first_frame) { + auth_value->first_frame = pinfo->fd->num; + } + return auth_value; +} + +static void +dissect_dcerpc_cn_auth(tvbuff_t *tvb, int stub_offset, packet_info *pinfo, + proto_tree *dcerpc_tree, e_dce_cn_common_hdr_t *hdr, + dcerpc_auth_info *auth_info) +{ + volatile int offset; + + /* + * Initially set auth_level and auth_type to zero to indicate that we + * haven't yet seen any authentication level information. + */ + auth_info->hdr_signing = FALSE; + auth_info->auth_type = 0; + auth_info->auth_level = 0; + auth_info->auth_context_id = 0; + auth_info->auth_pad_len = 0; + auth_info->auth_size = 0; + auth_info->auth_fns = NULL; + auth_info->auth_tvb = NULL; + auth_info->auth_item = NULL; + auth_info->auth_tree = NULL; + auth_info->auth_hdr_tvb = NULL; + + /* + * The authentication information is at the *end* of the PDU; in + * request and response PDUs, the request and response stub data + * come before it. + * + * Is there any authentication data (i.e., is the authentication length + * non-zero), and is the authentication length valid (i.e., is it, plus + * 8 bytes for the type/level/pad length/reserved/context id, less than + * or equal to the fragment length minus the starting offset of the + * stub data?) + */ + + if (hdr->auth_len + && ((hdr->auth_len + 8) <= (hdr->frag_len - stub_offset))) { + + /* + * Yes, there is authentication data, and the length is valid. + * Do we have all the bytes of stub data? + * (If not, we'd throw an exception dissecting *that*, so don't + * bother trying to dissect the authentication information and + * throwing another exception there.) + */ + offset = hdr->frag_len - (hdr->auth_len + 8); + if (offset == 0 || tvb_offset_exists(tvb, offset - 1)) { + dcerpc_auth_context *auth_context = NULL; + int auth_offset = offset; + + /* Compute the size of the auth block. Note that this should not + include auth padding, since when NTLMSSP encryption is used, the + padding is actually inside the encrypted stub */ + auth_info->auth_size = hdr->auth_len + 8; + + auth_info->auth_item = proto_tree_add_item(dcerpc_tree, hf_dcerpc_auth_info, + tvb, offset, auth_info->auth_size, ENC_NA); + auth_info->auth_tree = proto_item_add_subtree(auth_info->auth_item, ett_dcerpc_auth_info); + + /* + * Either there's no stub data, or the last byte of the stub + * data is present in the captured data, so we shouldn't + * get a BoundsError dissecting the stub data. + * + * Try dissecting the authentication data. + * Catch all exceptions, so that even if the auth info is bad + * or we don't have all of it, we still show the stuff we + * dissect after this, such as stub data. + */ + TRY { + offset = dissect_dcerpc_uint8(tvb, offset, pinfo, auth_info->auth_tree, hdr->drep, + hf_dcerpc_auth_type, + &auth_info->auth_type); + offset = dissect_dcerpc_uint8(tvb, offset, pinfo, auth_info->auth_tree, hdr->drep, + hf_dcerpc_auth_level, + &auth_info->auth_level); + + offset = dissect_dcerpc_uint8(tvb, offset, pinfo, auth_info->auth_tree, hdr->drep, + hf_dcerpc_auth_pad_len, + &auth_info->auth_pad_len); + offset = dissect_dcerpc_uint8(tvb, offset, pinfo, auth_info->auth_tree, hdr->drep, + hf_dcerpc_auth_rsrvd, NULL); + offset = dissect_dcerpc_uint32(tvb, offset, pinfo, auth_info->auth_tree, hdr->drep, + hf_dcerpc_auth_ctx_id, + &auth_info->auth_context_id); + + proto_item_append_text(auth_info->auth_item, + ": %s, %s, AuthContextId(%d)", + val_to_str(auth_info->auth_type, + authn_protocol_vals, + "AuthType(%u)"), + val_to_str(auth_info->auth_level, + authn_level_vals, + "AuthLevel(%u)"), + auth_info->auth_context_id); + + /* + * Dissect the authentication data. + */ + auth_info->auth_hdr_tvb = tvb_new_subset_length_caplen(tvb, auth_offset, 8, 8); + auth_info->auth_tvb = tvb_new_subset_length_caplen(tvb, offset, + MIN(hdr->auth_len,tvb_reported_length_remaining(tvb, offset)), + hdr->auth_len); + + auth_context = find_or_create_dcerpc_auth_context(pinfo, auth_info); + if (auth_context != NULL) { + if (hdr->ptype == PDU_BIND || hdr->ptype == PDU_ALTER) { + if (auth_context->first_frame == pinfo->fd->num) { + auth_context->hdr_signing = (hdr->flags & PFC_HDR_SIGNING); + } + } + + auth_info->hdr_signing = auth_context->hdr_signing; + } + + auth_info->auth_fns = get_auth_subdissector_fns(auth_info->auth_level, + auth_info->auth_type); + if (auth_info->auth_fns != NULL) + dissect_auth_verf(pinfo, hdr, auth_info); + else + proto_tree_add_item(auth_info->auth_tree, + hf_dcerpc_auth_credentials, + auth_info->auth_tvb, 0, + hdr->auth_len, ENC_NA); + + } CATCH_BOUNDS_ERRORS { + show_exception(tvb, pinfo, dcerpc_tree, EXCEPT_CODE, GET_MESSAGE); + } ENDTRY; + } + } +} + + +/* We need to hash in the SMB fid number to generate a unique hash table + * key as DCERPC over SMB allows several pipes over the same TCP/IP + * socket. + * We pass this function the transport type here to make sure we only look + * at this function if it came across an SMB pipe. + * Other transports might need to mix in their own extra multiplexing data + * as well in the future. + */ + +guint64 +dcerpc_get_transport_salt(packet_info *pinfo) +{ + dcerpc_decode_as_data* decode_data = dcerpc_get_decode_data(pinfo); + + switch (decode_data->dcetransporttype) { + case DCE_CN_TRANSPORT_SMBPIPE: + /* DCERPC over smb */ + return decode_data->dcetransportsalt; + } + + /* Some other transport... */ + return 0; +} + +void +dcerpc_set_transport_salt(guint64 dcetransportsalt, packet_info *pinfo) +{ + dcerpc_decode_as_data* decode_data = dcerpc_get_decode_data(pinfo); + + decode_data->dcetransportsalt = dcetransportsalt; +} + +/* + * Connection oriented packet types + */ + +static void +dissect_dcerpc_cn_bind(tvbuff_t *tvb, gint offset, packet_info *pinfo, + proto_tree *dcerpc_tree, e_dce_cn_common_hdr_t *hdr) +{ + conversation_t *conv = find_or_create_conversation(pinfo); + guint8 num_ctx_items = 0; + guint i; + guint16 ctx_id; + guint8 num_trans_items; + guint j; + e_guid_t if_id; + e_guid_t trans_id; + guint32 trans_ver; + guint16 if_ver, if_ver_minor; + dcerpc_auth_info auth_info; + char *uuid_str; + const char *uuid_name = NULL; + proto_item *iface_item = NULL; + dcerpc_decode_as_data* decode_data = dcerpc_get_decode_data(pinfo); + + offset = dissect_dcerpc_uint16(tvb, offset, pinfo, dcerpc_tree, hdr->drep, + hf_dcerpc_cn_max_xmit, NULL); + + offset = dissect_dcerpc_uint16(tvb, offset, pinfo, dcerpc_tree, hdr->drep, + hf_dcerpc_cn_max_recv, NULL); + + offset = dissect_dcerpc_uint32(tvb, offset, pinfo, dcerpc_tree, hdr->drep, + hf_dcerpc_cn_assoc_group, NULL); + + offset = dissect_dcerpc_uint8(tvb, offset, pinfo, dcerpc_tree, hdr->drep, + hf_dcerpc_cn_num_ctx_items, &num_ctx_items); + + /* padding */ + offset += 3; + + col_append_fstr(pinfo->cinfo, COL_INFO, ", %u context items:", num_ctx_items); + + for (i = 0; i < num_ctx_items; i++) { + proto_item *ctx_item = NULL; + proto_tree *ctx_tree = NULL, *iface_tree = NULL; + gint ctx_offset = offset; + + dissect_dcerpc_uint16(tvb, offset, pinfo, NULL, hdr->drep, + hf_dcerpc_cn_ctx_id, &ctx_id); + + /* save context ID for use with dcerpc_add_conv_to_bind_table() */ + /* (if we have multiple contexts, this might cause "decode as" + * to behave unpredictably) */ + decode_data->dcectxid = ctx_id; + + if (dcerpc_tree) { + ctx_item = proto_tree_add_item(dcerpc_tree, hf_dcerpc_cn_ctx_item, + tvb, offset, 0, + ENC_NA); + ctx_tree = proto_item_add_subtree(ctx_item, ett_dcerpc_cn_ctx); + } + + offset = dissect_dcerpc_uint16(tvb, offset, pinfo, ctx_tree, hdr->drep, + hf_dcerpc_cn_ctx_id, &ctx_id); + offset = dissect_dcerpc_uint8(tvb, offset, pinfo, ctx_tree, hdr->drep, + hf_dcerpc_cn_num_trans_items, &num_trans_items); + + if (dcerpc_tree) { + proto_item_append_text(ctx_item, "[%u]: Context ID:%u", i+1, ctx_id); + } + + /* padding */ + offset += 1; + + dcerpc_tvb_get_uuid(tvb, offset, hdr->drep, &if_id); + if (ctx_tree) { + + iface_item = proto_tree_add_item(ctx_tree, hf_dcerpc_cn_bind_abstract_syntax, tvb, offset, 0, ENC_NA); + iface_tree = proto_item_add_subtree(iface_item, ett_dcerpc_cn_iface); + + uuid_str = guid_to_str(pinfo->pool, (e_guid_t*)&if_id); + uuid_name = guids_get_uuid_name(&if_id, pinfo->pool); + if (uuid_name) { + proto_tree_add_guid_format(iface_tree, hf_dcerpc_cn_bind_if_id, tvb, + offset, 16, (e_guid_t *) &if_id, "Interface: %s UUID: %s", uuid_name, uuid_str); + proto_item_append_text(iface_item, ": %s", uuid_name); + proto_item_append_text(ctx_item, ", %s", uuid_name); + } else { + proto_tree_add_guid_format(iface_tree, hf_dcerpc_cn_bind_if_id, tvb, + offset, 16, (e_guid_t *) &if_id, "Interface UUID: %s", uuid_str); + proto_item_append_text(iface_item, ": %s", uuid_str); + proto_item_append_text(ctx_item, ", %s", uuid_str); + } + } + offset += 16; + + if (hdr->drep[0] & DREP_LITTLE_ENDIAN) { + offset = dissect_dcerpc_uint16(tvb, offset, pinfo, iface_tree, hdr->drep, + hf_dcerpc_cn_bind_if_ver, &if_ver); + offset = dissect_dcerpc_uint16(tvb, offset, pinfo, iface_tree, hdr->drep, + hf_dcerpc_cn_bind_if_ver_minor, &if_ver_minor); + } else { + offset = dissect_dcerpc_uint16(tvb, offset, pinfo, iface_tree, hdr->drep, + hf_dcerpc_cn_bind_if_ver_minor, &if_ver_minor); + offset = dissect_dcerpc_uint16(tvb, offset, pinfo, iface_tree, hdr->drep, + hf_dcerpc_cn_bind_if_ver, &if_ver); + } + + if (ctx_tree) { + proto_item_append_text(iface_item, " V%u.%u", if_ver, if_ver_minor); + proto_item_set_len(iface_item, 20); + } + + memset(&trans_id, 0, sizeof(trans_id)); + for (j = 0; j < num_trans_items; j++) { + proto_tree *trans_tree = NULL; + proto_item *trans_item = NULL; + + dcerpc_tvb_get_uuid(tvb, offset, hdr->drep, &trans_id); + if (ctx_tree) { + + trans_item = proto_tree_add_item(ctx_tree, hf_dcerpc_cn_bind_trans_syntax, tvb, offset, 0, ENC_NA); + trans_tree = proto_item_add_subtree(trans_item, ett_dcerpc_cn_trans_syntax); + + uuid_str = guid_to_str(pinfo->pool, (e_guid_t *) &trans_id); + uuid_name = guids_get_uuid_name(&trans_id, pinfo->pool); + + /* check for [MS-RPCE] 3.3.1.5.3 Bind Time Feature Negotiation */ + if (trans_id.data1 == 0x6cb71c2c && trans_id.data2 == 0x9812 && trans_id.data3 == 0x4540) { + proto_tree_add_guid_format(trans_tree, hf_dcerpc_cn_bind_trans_id, + tvb, offset, 16, (e_guid_t *) &trans_id, + "Transfer Syntax: Bind Time Feature Negotiation UUID:%s", + uuid_str); + proto_tree_add_bitmask(trans_tree, tvb, offset + 8, + hf_dcerpc_cn_bind_trans_btfn, + ett_dcerpc_cn_bind_trans_btfn, + dcerpc_cn_bind_trans_btfn_fields, + ENC_LITTLE_ENDIAN); + proto_item_append_text(trans_item, "[%u]: Bind Time Feature Negotiation", j+1); + proto_item_append_text(ctx_item, ", Bind Time Feature Negotiation"); + } else if (uuid_name) { + proto_tree_add_guid_format(trans_tree, hf_dcerpc_cn_bind_trans_id, + tvb, offset, 16, (e_guid_t *) &trans_id, + "Transfer Syntax: %s UUID:%s", uuid_name, uuid_str); + proto_item_append_text(trans_item, "[%u]: %s", j+1, uuid_name); + proto_item_append_text(ctx_item, ", %s", uuid_name); + } else { + proto_tree_add_guid_format(trans_tree, hf_dcerpc_cn_bind_trans_id, + tvb, offset, 16, (e_guid_t *) &trans_id, + "Transfer Syntax: %s", uuid_str); + proto_item_append_text(trans_item, "[%u]: %s", j+1, uuid_str); + proto_item_append_text(ctx_item, ", %s", uuid_str); + } + + } + offset += 16; + + offset = dissect_dcerpc_uint32(tvb, offset, pinfo, trans_tree, hdr->drep, + hf_dcerpc_cn_bind_trans_ver, &trans_ver); + if (ctx_tree) { + proto_item_set_len(trans_item, 20); + proto_item_append_text(trans_item, " V%u", trans_ver); + } + } + + /* if this is the first time we've seen this packet, we need to + update the dcerpc_binds table so that any later calls can + match to the interface. + XXX We assume that BINDs will NEVER be fragmented. + */ + if (!(pinfo->fd->visited)) { + dcerpc_bind_key *key; + dcerpc_bind_value *value; + + key = wmem_new(wmem_file_scope(), dcerpc_bind_key); + key->conv = conv; + key->ctx_id = ctx_id; + key->transport_salt = dcerpc_get_transport_salt(pinfo); + + value = wmem_new(wmem_file_scope(), dcerpc_bind_value); + value->uuid = if_id; + value->ver = if_ver; + value->transport = trans_id; + + /* add this entry to the bind table */ + wmem_map_insert(dcerpc_binds, key, value); + } + + if (i > 0) + col_append_fstr(pinfo->cinfo, COL_INFO, ","); + col_append_fstr(pinfo->cinfo, COL_INFO, " %s V%u.%u (%s)", + guids_resolve_guid_to_str(&if_id, pinfo->pool), if_ver, if_ver_minor, + guids_resolve_guid_to_str(&trans_id, pinfo->pool)); + + if (ctx_tree) { + proto_item_set_len(ctx_item, offset - ctx_offset); + } + } + + /* + * XXX - we should save the authentication type *if* we have + * an authentication header, and associate it with an authentication + * context, so subsequent PDUs can use that context. + */ + dissect_dcerpc_cn_auth(tvb, offset, pinfo, dcerpc_tree, hdr, &auth_info); +} + +static void +dissect_dcerpc_cn_bind_ack(tvbuff_t *tvb, gint offset, packet_info *pinfo, + proto_tree *dcerpc_tree, e_dce_cn_common_hdr_t *hdr) +{ + guint16 max_xmit, max_recv; + guint16 sec_addr_len; + guint8 num_results; + guint i; + guint16 result = 0; + guint16 reason = 0; + e_guid_t trans_id; + guint32 trans_ver; + dcerpc_auth_info auth_info; + const char *uuid_name = NULL; + const char *result_str = NULL; + + offset = dissect_dcerpc_uint16(tvb, offset, pinfo, dcerpc_tree, hdr->drep, + hf_dcerpc_cn_max_xmit, &max_xmit); + + offset = dissect_dcerpc_uint16(tvb, offset, pinfo, dcerpc_tree, hdr->drep, + hf_dcerpc_cn_max_recv, &max_recv); + + offset = dissect_dcerpc_uint32(tvb, offset, pinfo, dcerpc_tree, hdr->drep, + hf_dcerpc_cn_assoc_group, NULL); + + offset = dissect_dcerpc_uint16(tvb, offset, pinfo, dcerpc_tree, hdr->drep, + hf_dcerpc_cn_sec_addr_len, &sec_addr_len); + if (sec_addr_len != 0) { + proto_tree_add_item(dcerpc_tree, hf_dcerpc_cn_sec_addr, tvb, offset, + sec_addr_len, ENC_ASCII); + offset += sec_addr_len; + } + + if (offset % 4) { + offset += 4 - offset % 4; + } + + offset = dissect_dcerpc_uint8(tvb, offset, pinfo, dcerpc_tree, hdr->drep, + hf_dcerpc_cn_num_results, &num_results); + + /* padding */ + offset += 3; + + col_append_fstr(pinfo->cinfo, COL_INFO, ", max_xmit: %u max_recv: %u, %u results:", + max_xmit, max_recv, num_results); + + for (i = 0; i < num_results; i++) { + proto_tree *ctx_tree = NULL; + proto_item *ctx_item = NULL; + + if (dcerpc_tree) { + ctx_tree = proto_tree_add_subtree_format(dcerpc_tree, tvb, offset, 24, ett_dcerpc_cn_ctx, &ctx_item, "Ctx Item[%u]:", i+1); + } + + offset = dissect_dcerpc_uint16(tvb, offset, pinfo, ctx_tree, + hdr->drep, hf_dcerpc_cn_ack_result, + &result); + + /* [MS-RPCE] 3.3.1.5.3 check if this Ctx Item is the response to a Bind Time Feature Negotiation request */ + if (result == 3) { + proto_tree_add_bitmask(ctx_tree, tvb, offset, + hf_dcerpc_cn_bind_trans_btfn, + ett_dcerpc_cn_bind_trans_btfn, + dcerpc_cn_bind_trans_btfn_fields, + ENC_LITTLE_ENDIAN); + offset += 2; + } else if (result != 0) { + offset = dissect_dcerpc_uint16(tvb, offset, pinfo, ctx_tree, + hdr->drep, hf_dcerpc_cn_ack_reason, + &reason); + } else { + /* + * The reason for rejection isn't meaningful, and often isn't + * set, when the syntax was accepted. + */ + offset += 2; + } + + result_str = val_to_str(result, p_cont_result_vals, "Unknown result (%u)"); + + if (ctx_tree) { + dcerpc_tvb_get_uuid(tvb, offset, hdr->drep, &trans_id); + uuid_name = guids_get_uuid_name(&trans_id, pinfo->pool); + if (! uuid_name) { + uuid_name = guid_to_str(pinfo->pool, (e_guid_t *) &trans_id); + } + proto_tree_add_guid_format(ctx_tree, hf_dcerpc_cn_ack_trans_id, tvb, + offset, 16, (e_guid_t *) &trans_id, "Transfer Syntax: %s", + uuid_name); + proto_item_append_text(ctx_item, " %s, %s", result_str, uuid_name); + } + offset += 16; + + offset = dissect_dcerpc_uint32(tvb, offset, pinfo, ctx_tree, hdr->drep, + hf_dcerpc_cn_ack_trans_ver, &trans_ver); + + if (i > 0) + col_append_fstr(pinfo->cinfo, COL_INFO, ","); + col_append_fstr(pinfo->cinfo, COL_INFO, " %s", result_str); + } + + /* + * XXX - do we need to do anything with the authentication level + * we get back from this? + */ + dissect_dcerpc_cn_auth(tvb, offset, pinfo, dcerpc_tree, hdr, &auth_info); +} + +static void +dissect_dcerpc_cn_bind_nak(tvbuff_t *tvb, gint offset, packet_info *pinfo, + proto_tree *dcerpc_tree, e_dce_cn_common_hdr_t *hdr) +{ + guint16 reason; + guint8 num_protocols; + guint i; + + offset = dissect_dcerpc_uint16(tvb, offset, pinfo, dcerpc_tree, + hdr->drep, hf_dcerpc_cn_reject_reason, + &reason); + + col_append_fstr(pinfo->cinfo, COL_INFO, " reason: %s", + val_to_str(reason, reject_reason_vals, "Unknown (%u)")); + + if (reason == PROTOCOL_VERSION_NOT_SUPPORTED) { + offset = dissect_dcerpc_uint8(tvb, offset, pinfo, dcerpc_tree, hdr->drep, + hf_dcerpc_cn_num_protocols, + &num_protocols); + + for (i = 0; i < num_protocols; i++) { + offset = dissect_dcerpc_uint8(tvb, offset, pinfo, dcerpc_tree, + hdr->drep, hf_dcerpc_cn_protocol_ver_major, + NULL); + offset = dissect_dcerpc_uint8(tvb, offset, pinfo, dcerpc_tree, + hdr->drep, hf_dcerpc_cn_protocol_ver_minor, + NULL); + } + } +} + +/* Return a string describing a DCE/RPC fragment as first, middle, or end + fragment. */ + +#define PFC_FRAG_MASK 0x03 + +static const char * +fragment_type(guint8 flags) +{ + static const char* t[4] = { + "Mid", + "1st", + "Last", + "Single" + }; + return t[flags & PFC_FRAG_MASK]; +} + +/* Dissect stub data (payload) of a DCERPC packet. */ + +static void +dissect_dcerpc_cn_stub(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *dcerpc_tree, proto_tree *tree, + e_dce_cn_common_hdr_t *hdr, dcerpc_info *di, + dcerpc_auth_info *auth_info, guint32 alloc_hint _U_, + guint32 frame) +{ + gint length, reported_length; + gboolean save_fragmented; + fragment_head *fd_head = NULL; + + tvbuff_t *header_tvb = NULL, *trailer_tvb = NULL; + tvbuff_t *payload_tvb, *decrypted_tvb = NULL; + proto_item *pi; + proto_item *parent_pi; + proto_item *dcerpc_tree_item; + + save_fragmented = pinfo->fragmented; + + length = tvb_reported_length_remaining(tvb, offset); + reported_length = tvb_reported_length_remaining(tvb, offset); + if (reported_length < 0 || + (guint32)reported_length < auth_info->auth_size) { + /* We don't even have enough bytes for the authentication + stuff. */ + return; + } + reported_length -= auth_info->auth_size; + if (length > reported_length) + length = reported_length; + header_tvb = tvb_new_subset_length_caplen(tvb, 0, offset, offset); + payload_tvb = tvb_new_subset_length_caplen(tvb, offset, length, reported_length); + trailer_tvb = auth_info->auth_hdr_tvb; + + /* Decrypt the PDU if it is encrypted */ + + if (auth_info->auth_type && + (auth_info->auth_level == DCE_C_AUTHN_LEVEL_PKT_PRIVACY)) { + + /* Start out assuming we won't succeed in decrypting. */ + + if (auth_info->auth_fns != NULL) { + tvbuff_t *result; + + result = decode_encrypted_data(header_tvb, payload_tvb, trailer_tvb, + pinfo, hdr, auth_info); + if (result) { + proto_tree_add_item(dcerpc_tree, hf_dcerpc_encrypted_stub_data, payload_tvb, 0, -1, ENC_NA); + + add_new_data_source( + pinfo, result, "Decrypted stub data"); + + /* We succeeded. */ + decrypted_tvb = result; + } + } + } else + decrypted_tvb = payload_tvb; + + /* if this packet is not fragmented, just dissect it and exit */ + if (PFC_NOT_FRAGMENTED(hdr)) { + pinfo->fragmented = FALSE; + + dcerpc_try_handoff(pinfo, tree, dcerpc_tree, + ((decrypted_tvb != NULL) ? decrypted_tvb : payload_tvb), + ((decrypted_tvb != NULL) ? TRUE : FALSE), + hdr->drep, di, auth_info); + + pinfo->fragmented = save_fragmented; + return; + } + + /* The packet is fragmented. */ + pinfo->fragmented = TRUE; + + /* debug output of essential fragment data. */ + /* leave it here for future debugging sessions */ + /*printf("DCE num:%u offset:%u frag_len:%u tvb_len:%u\n", + pinfo->num, offset, hdr->frag_len, tvb_reported_length(decrypted_tvb));*/ + + /* if we are not doing reassembly and this is the first fragment + then just dissect it and exit + XXX - if we're not doing reassembly, can we decrypt an + encrypted stub? + */ + if ( (!dcerpc_reassemble) && (hdr->flags & PFC_FIRST_FRAG) ) { + + dcerpc_try_handoff(pinfo, tree, dcerpc_tree, + ((decrypted_tvb != NULL) ? decrypted_tvb : payload_tvb), + ((decrypted_tvb != NULL) ? TRUE : FALSE), + hdr->drep, di, auth_info); + + expert_add_info_format(pinfo, NULL, &ei_dcerpc_fragment, "%s fragment", fragment_type(hdr->flags)); + + pinfo->fragmented = save_fragmented; + return; + } + + /* if we have already seen this packet, see if it was reassembled + and if so dissect the full pdu. + then exit + */ + if (pinfo->fd->visited) { + fd_head = fragment_get_reassembled_id(&dcerpc_co_reassembly_table, pinfo, frame); + goto end_cn_stub; + } + + /* if we are not doing reassembly and it was neither a complete PDU + nor the first fragment then there is nothing more we can do + so we just have to exit + */ + if ( !dcerpc_reassemble || (tvb_captured_length(tvb) != tvb_reported_length(tvb)) ) + goto end_cn_stub; + + /* if we didn't get 'frame' we don't know where the PDU started and thus + it is pointless to continue + */ + if (!frame) + goto end_cn_stub; + + /* from now on we must attempt to reassemble the PDU + */ + + /* if we get here we know it is the first time we see the packet + and we also know it is only a fragment and not a full PDU, + thus we must reassemble it. + */ + + /* Do we have any non-encrypted data to reassemble? */ + if (decrypted_tvb == NULL) { + /* No. We can't even try to reassemble. */ + goto end_cn_stub; + } + + /* defragmentation is a bit tricky, as there's no offset of the fragment + * in the protocol data. + * + * just use fragment_add_seq_next() and hope that TCP/SMB segments coming + * in with the correct sequence. + */ + fd_head = fragment_add_seq_next(&dcerpc_co_reassembly_table, + decrypted_tvb, 0, pinfo, frame, NULL, + tvb_reported_length(decrypted_tvb), + !(hdr->flags & PFC_LAST_FRAG) /* more_frags */); + +end_cn_stub: + + /* if reassembly is complete and this is the last fragment + * (multiple fragments in one PDU are possible!) + * dissect the full PDU + */ + if (fd_head && (fd_head->flags & FD_DEFRAGMENTED) ) { + + if ((pinfo->num == fd_head->reassembled_in) && (hdr->flags & PFC_LAST_FRAG) ) { + tvbuff_t *next_tvb; + proto_item *frag_tree_item; + + next_tvb = tvb_new_chain((decrypted_tvb)?decrypted_tvb:payload_tvb, + fd_head->tvb_data); + + add_new_data_source(pinfo, next_tvb, "Reassembled DCE/RPC"); + show_fragment_tree(fd_head, &dcerpc_frag_items, + tree, pinfo, next_tvb, &frag_tree_item); + /* the toplevel fragment subtree is now behind all desegmented data, + * move it right behind the DCE/RPC tree */ + dcerpc_tree_item = proto_tree_get_parent(dcerpc_tree); + if (frag_tree_item && dcerpc_tree_item) { + proto_tree_move_item(tree, dcerpc_tree_item, frag_tree_item); + } + + pinfo->fragmented = FALSE; + + expert_add_info_format(pinfo, frag_tree_item, &ei_dcerpc_fragment_reassembled, "%s fragment, reassembled", fragment_type(hdr->flags)); + + dcerpc_try_handoff(pinfo, tree, dcerpc_tree, next_tvb, TRUE, hdr->drep, di, auth_info); + + } else { + if (decrypted_tvb) { + pi = proto_tree_add_uint(dcerpc_tree, hf_dcerpc_reassembled_in, + decrypted_tvb, 0, 0, fd_head->reassembled_in); + } else { + pi = proto_tree_add_uint(dcerpc_tree, hf_dcerpc_reassembled_in, + payload_tvb, 0, 0, fd_head->reassembled_in); + } + proto_item_set_generated(pi); + parent_pi = proto_tree_get_parent(dcerpc_tree); + if (parent_pi != NULL) { + proto_item_append_text(parent_pi, ", [Reas: #%u]", fd_head->reassembled_in); + } + col_append_fstr(pinfo->cinfo, COL_INFO, + " [DCE/RPC %s fragment, reas: #%u]", fragment_type(hdr->flags), fd_head->reassembled_in); + expert_add_info_format(pinfo, NULL, &ei_dcerpc_fragment_reassembled, "%s fragment, reassembled in #%u", fragment_type(hdr->flags), fd_head->reassembled_in); + } + } else { + /* Reassembly not complete - some fragments + are missing. Just show the stub data. */ + expert_add_info_format(pinfo, NULL, &ei_dcerpc_fragment, "%s fragment", fragment_type(hdr->flags)); + + if (decrypted_tvb) { + show_stub_data(pinfo, decrypted_tvb, 0, tree, auth_info, FALSE); + } else { + show_stub_data(pinfo, payload_tvb, 0, tree, auth_info, TRUE); + } + } + + pinfo->fragmented = save_fragmented; +} + +static void +dissect_dcerpc_cn_rqst(tvbuff_t *tvb, gint offset, packet_info *pinfo, + proto_tree *dcerpc_tree, proto_tree *tree, + e_dce_cn_common_hdr_t *hdr) +{ + conversation_t *conv; + guint16 ctx_id; + guint16 opnum; + e_guid_t obj_id = DCERPC_UUID_NULL; + dcerpc_auth_info auth_info; + guint32 alloc_hint; + proto_item *pi; + proto_item *parent_pi; + dcerpc_decode_as_data* decode_data = dcerpc_get_decode_data(pinfo); + + offset = dissect_dcerpc_uint32(tvb, offset, pinfo, dcerpc_tree, hdr->drep, + hf_dcerpc_cn_alloc_hint, &alloc_hint); + + offset = dissect_dcerpc_uint16(tvb, offset, pinfo, dcerpc_tree, hdr->drep, + hf_dcerpc_cn_ctx_id, &ctx_id); + parent_pi = proto_tree_get_parent(dcerpc_tree); + if (parent_pi != NULL) { + proto_item_append_text(parent_pi, ", Ctx: %u", ctx_id); + } + + offset = dissect_dcerpc_uint16(tvb, offset, pinfo, dcerpc_tree, hdr->drep, + hf_dcerpc_opnum, &opnum); + + /* save context ID for use with dcerpc_add_conv_to_bind_table() */ + decode_data->dcectxid = ctx_id; + + col_append_fstr(pinfo->cinfo, COL_INFO, ", opnum: %u, Ctx: %u", + opnum, ctx_id); + + if (hdr->flags & PFC_OBJECT_UUID) { + dcerpc_tvb_get_uuid(tvb, offset, hdr->drep, &obj_id); + if (dcerpc_tree) { + proto_tree_add_guid_format(dcerpc_tree, hf_dcerpc_obj_id, tvb, + offset, 16, (e_guid_t *) &obj_id, "Object UUID: %s", + guid_to_str(pinfo->pool, (e_guid_t *) &obj_id)); + } + offset += 16; + } + + /* + * XXX - what if this was set when the connection was set up, + * and we just have a security context? + */ + dissect_dcerpc_cn_auth(tvb, offset, pinfo, dcerpc_tree, hdr, &auth_info); + + conv = find_conversation_pinfo(pinfo, 0); + if (!conv) + show_stub_data(pinfo, tvb, offset, dcerpc_tree, &auth_info, TRUE); + else { + dcerpc_matched_key matched_key, *new_matched_key; + dcerpc_call_value *value; + + /* !!! we can NOT check visited here since this will interact + badly with when SMB handles (i.e. calls the subdissector) + and desegmented pdu's . + Instead we check if this pdu is already in the matched table or not + */ + matched_key.frame = pinfo->num; + matched_key.call_id = hdr->call_id; + value = (dcerpc_call_value *)wmem_map_lookup(dcerpc_matched, &matched_key); + if (!value) { + dcerpc_bind_key bind_key; + dcerpc_bind_value *bind_value; + + bind_key.conv = conv; + bind_key.ctx_id = ctx_id; + bind_key.transport_salt = dcerpc_get_transport_salt(pinfo); + + if ((bind_value = (dcerpc_bind_value *)wmem_map_lookup(dcerpc_binds, &bind_key)) ) { + if (!(hdr->flags&PFC_FIRST_FRAG)) { + dcerpc_cn_call_key call_key; + dcerpc_call_value *call_value; + + call_key.conv = conv; + call_key.call_id = hdr->call_id; + call_key.transport_salt = dcerpc_get_transport_salt(pinfo); + if ((call_value = (dcerpc_call_value *)wmem_map_lookup(dcerpc_cn_calls, &call_key))) { + new_matched_key = wmem_new(wmem_file_scope(), dcerpc_matched_key); + *new_matched_key = matched_key; + wmem_map_insert(dcerpc_matched, new_matched_key, call_value); + value = call_value; + } + } else { + dcerpc_cn_call_key *call_key; + dcerpc_call_value *call_value; + + /* We found the binding and it is the first fragment + (or a complete PDU) of a dcerpc pdu so just add + the call to both the call table and the + matched table + */ + call_key = wmem_new(wmem_file_scope(), dcerpc_cn_call_key); + call_key->conv = conv; + call_key->call_id = hdr->call_id; + call_key->transport_salt = dcerpc_get_transport_salt(pinfo); + + /* if there is already a matching call in the table + remove it so it is replaced with the new one */ + if (wmem_map_lookup(dcerpc_cn_calls, call_key)) { + wmem_map_remove(dcerpc_cn_calls, call_key); + } + + call_value = wmem_new(wmem_file_scope(), dcerpc_call_value); + call_value->uuid = bind_value->uuid; + call_value->ver = bind_value->ver; + call_value->object_uuid = obj_id; + call_value->opnum = opnum; + call_value->req_frame = pinfo->num; + call_value->req_time = pinfo->abs_ts; + call_value->rep_frame = 0; + call_value->max_ptr = 0; + call_value->se_data = NULL; + call_value->private_data = NULL; + call_value->pol = NULL; + call_value->flags = 0; + if (!memcmp(&bind_value->transport, &uuid_ndr64, sizeof(uuid_ndr64))) { + call_value->flags |= DCERPC_IS_NDR64; + } + + wmem_map_insert(dcerpc_cn_calls, call_key, call_value); + + new_matched_key = wmem_new(wmem_file_scope(), dcerpc_matched_key); + *new_matched_key = matched_key; + wmem_map_insert(dcerpc_matched, new_matched_key, call_value); + value = call_value; + } + } + } + + if (value) { + dcerpc_info *di; + + di = wmem_new0(pinfo->pool, dcerpc_info); + /* handoff this call */ + di->dcerpc_procedure_name = ""; + di->conv = conv; + di->call_id = hdr->call_id; + di->transport_salt = dcerpc_get_transport_salt(pinfo); + di->ptype = PDU_REQ; + di->call_data = value; + di->hf_index = -1; + + if (value->rep_frame != 0) { + pi = proto_tree_add_uint(dcerpc_tree, hf_dcerpc_response_in, + tvb, 0, 0, value->rep_frame); + proto_item_set_generated(pi); + if (parent_pi != NULL) { + proto_item_append_text(parent_pi, ", [Resp: #%u]", value->rep_frame); + } + } + + dissect_dcerpc_cn_stub(tvb, offset, pinfo, dcerpc_tree, tree, + hdr, di, &auth_info, alloc_hint, + value->req_frame); + } else { + /* no bind information, simply show stub data */ + proto_tree_add_expert_format(dcerpc_tree, pinfo, &ei_dcerpc_cn_ctx_id_no_bind, tvb, offset, 0, "No bind info for interface Context ID %u - capture start too late?", ctx_id); + show_stub_data(pinfo, tvb, offset, dcerpc_tree, &auth_info, TRUE); + } + } + + /* + * Move the auth_info subtree to the end, + * as it's also at the end of the pdu on the wire. + */ + dissect_dcerpc_cn_auth_move(&auth_info, dcerpc_tree); +} + +static void +dissect_dcerpc_cn_resp(tvbuff_t *tvb, gint offset, packet_info *pinfo, + proto_tree *dcerpc_tree, proto_tree *tree, + e_dce_cn_common_hdr_t *hdr) +{ + dcerpc_call_value *value = NULL; + conversation_t *conv; + guint16 ctx_id; + dcerpc_auth_info auth_info; + guint32 alloc_hint; + proto_item *pi; + proto_item *parent_pi; + e_guid_t obj_id_null = DCERPC_UUID_NULL; + dcerpc_decode_as_data* decode_data = dcerpc_get_decode_data(pinfo); + + offset = dissect_dcerpc_uint32(tvb, offset, pinfo, dcerpc_tree, hdr->drep, + hf_dcerpc_cn_alloc_hint, &alloc_hint); + + offset = dissect_dcerpc_uint16(tvb, offset, pinfo, dcerpc_tree, hdr->drep, + hf_dcerpc_cn_ctx_id, &ctx_id); + parent_pi = proto_tree_get_parent(dcerpc_tree); + if (parent_pi != NULL) { + proto_item_append_text(parent_pi, ", Ctx: %u", ctx_id); + } + + /* save context ID for use with dcerpc_add_conv_to_bind_table() */ + decode_data->dcectxid = ctx_id; + + col_append_fstr(pinfo->cinfo, COL_INFO, ", Ctx: %u", ctx_id); + + offset = dissect_dcerpc_uint8(tvb, offset, pinfo, dcerpc_tree, hdr->drep, + hf_dcerpc_cn_cancel_count, NULL); + /* padding */ + offset++; + + /* + * XXX - what if this was set when the connection was set up, + * and we just have a security context? + */ + dissect_dcerpc_cn_auth(tvb, offset, pinfo, dcerpc_tree, hdr, &auth_info); + + conv = find_conversation_pinfo(pinfo, 0); + + if (!conv) { + /* no point in creating one here, really */ + show_stub_data(pinfo, tvb, offset, dcerpc_tree, &auth_info, TRUE); + } else { + dcerpc_matched_key matched_key, *new_matched_key; + + /* !!! we can NOT check visited here since this will interact + badly with when SMB handles (i.e. calls the subdissector) + and desegmented pdu's . + Instead we check if this pdu is already in the matched table or not + */ + matched_key.frame = pinfo->num; + matched_key.call_id = hdr->call_id; + value = (dcerpc_call_value *)wmem_map_lookup(dcerpc_matched, &matched_key); + if (!value) { + dcerpc_cn_call_key call_key; + dcerpc_call_value *call_value; + + call_key.conv = conv; + call_key.call_id = hdr->call_id; + call_key.transport_salt = dcerpc_get_transport_salt(pinfo); + + if ((call_value = (dcerpc_call_value *)wmem_map_lookup(dcerpc_cn_calls, &call_key))) { + /* extra sanity check, only match them if the reply + came after the request */ + if (call_value->req_frame<pinfo->num) { + new_matched_key = wmem_new(wmem_file_scope(), dcerpc_matched_key); + *new_matched_key = matched_key; + wmem_map_insert(dcerpc_matched, new_matched_key, call_value); + value = call_value; + if (call_value->rep_frame == 0) { + call_value->rep_frame = pinfo->num; + } + } + } + } + + if (value) { + dcerpc_info *di; + + di = wmem_new0(pinfo->pool, dcerpc_info); + /* handoff this call */ + di->dcerpc_procedure_name = ""; + di->conv = conv; + di->call_id = hdr->call_id; + di->transport_salt = dcerpc_get_transport_salt(pinfo); + di->ptype = PDU_RESP; + di->call_data = value; + + pi = proto_tree_add_uint(dcerpc_tree, hf_dcerpc_opnum, tvb, 0, 0, value->opnum); + proto_item_set_generated(pi); + + /* (optional) "Object UUID" from request */ + if (dcerpc_tree && (memcmp(&value->object_uuid, &obj_id_null, sizeof(obj_id_null)) != 0)) { + pi = proto_tree_add_guid_format(dcerpc_tree, hf_dcerpc_obj_id, tvb, + offset, 0, (e_guid_t *) &value->object_uuid, "Object UUID: %s", + guid_to_str(pinfo->pool, (e_guid_t *) &value->object_uuid)); + proto_item_set_generated(pi); + } + + /* request in */ + if (value->req_frame != 0) { + nstime_t delta_ts; + pi = proto_tree_add_uint(dcerpc_tree, hf_dcerpc_request_in, + tvb, 0, 0, value->req_frame); + proto_item_set_generated(pi); + if (parent_pi != NULL) { + proto_item_append_text(parent_pi, ", [Req: #%u]", value->req_frame); + } + nstime_delta(&delta_ts, &pinfo->abs_ts, &value->req_time); + pi = proto_tree_add_time(dcerpc_tree, hf_dcerpc_time, tvb, offset, 0, &delta_ts); + proto_item_set_generated(pi); + } else { + proto_tree_add_expert(dcerpc_tree, pinfo, &ei_dcerpc_no_request_found, tvb, 0, 0); + } + + dissect_dcerpc_cn_stub(tvb, offset, pinfo, dcerpc_tree, tree, + hdr, di, &auth_info, alloc_hint, + value->rep_frame); + } else { + /* no bind information, simply show stub data */ + proto_tree_add_expert_format(dcerpc_tree, pinfo, &ei_dcerpc_cn_ctx_id_no_bind, tvb, offset, 0, "No bind info for interface Context ID %u - capture start too late?", ctx_id); + show_stub_data(pinfo, tvb, offset, dcerpc_tree, &auth_info, TRUE); + } + } + + /* + * Move the auth_info subtree to the end, + * as it's also at the end of the pdu on the wire. + */ + dissect_dcerpc_cn_auth_move(&auth_info, dcerpc_tree); +} + +static void +dissect_dcerpc_cn_fault(tvbuff_t *tvb, gint offset, packet_info *pinfo, + proto_tree *dcerpc_tree, e_dce_cn_common_hdr_t *hdr) +{ + dcerpc_call_value *value = NULL; + conversation_t *conv; + guint16 ctx_id; + guint32 status; + guint32 alloc_hint; + dcerpc_auth_info auth_info; + gint length, reported_length; + tvbuff_t *stub_tvb = NULL; + proto_item *pi = NULL; + dcerpc_decode_as_data* decode_data = dcerpc_get_decode_data(pinfo); + + offset = dissect_dcerpc_uint32(tvb, offset, pinfo, dcerpc_tree, hdr->drep, + hf_dcerpc_cn_alloc_hint, &alloc_hint); + + offset = dissect_dcerpc_uint16(tvb, offset, pinfo, dcerpc_tree, hdr->drep, + hf_dcerpc_cn_ctx_id, &ctx_id); + + offset = dissect_dcerpc_uint8(tvb, offset, pinfo, dcerpc_tree, hdr->drep, + hf_dcerpc_cn_cancel_count, NULL); + proto_tree_add_bitmask(dcerpc_tree, tvb, offset, + hf_dcerpc_cn_fault_flags, + ett_dcerpc_fault_flags, + dcerpc_cn_fault_flags_fields, + DREP_ENC_INTEGER(hdr->drep)); + offset += 1; + +#if 0 + offset = dissect_dcerpc_uint32(tvb, offset, pinfo, dcerpc_tree, hdr->drep, + hf_dcerpc_cn_status, &status); +#endif + status = ((hdr->drep[0] & DREP_LITTLE_ENDIAN) + ? tvb_get_letohl(tvb, offset) + : tvb_get_ntohl(tvb, offset)); + + pi = proto_tree_add_item(dcerpc_tree, hf_dcerpc_cn_status, tvb, offset, 4, DREP_ENC_INTEGER(hdr->drep)); + offset+=4; + + expert_add_info_format(pinfo, pi, &ei_dcerpc_cn_status, "Fault: %s", val_to_str(status, reject_status_vals, "Unknown (0x%08x)")); + + /* save context ID for use with dcerpc_add_conv_to_bind_table() */ + decode_data->dcectxid = ctx_id; + + col_append_fstr(pinfo->cinfo, COL_INFO, + ", Ctx: %u, status: %s", ctx_id, + val_to_str(status, reject_status_vals, + "Unknown (0x%08x)")); + + /* padding */ + proto_tree_add_item(dcerpc_tree, hf_dcerpc_reserved, tvb, offset, 4, ENC_NA); + offset += 4; + + /* + * XXX - what if this was set when the connection was set up, + * and we just have a security context? + */ + dissect_dcerpc_cn_auth(tvb, offset, pinfo, dcerpc_tree, hdr, &auth_info); + + length = tvb_captured_length_remaining(tvb, offset); + reported_length = tvb_reported_length_remaining(tvb, offset); + if (reported_length < 0 || + (guint32)reported_length < auth_info.auth_size) { + /* We don't even have enough bytes for the authentication + stuff. */ + return; + } + reported_length -= auth_info.auth_size; + if (length > reported_length) + length = reported_length; + stub_tvb = tvb_new_subset_length_caplen(tvb, offset, length, reported_length); + + conv = find_conversation_pinfo(pinfo, 0); + if (!conv) { + /* no point in creating one here, really */ + } else { + dcerpc_matched_key matched_key, *new_matched_key; + + /* !!! we can NOT check visited here since this will interact + badly with when SMB handles (i.e. calls the subdissector) + and desegmented pdu's . + Instead we check if this pdu is already in the matched table or not + */ + matched_key.frame = pinfo->num; + matched_key.call_id = hdr->call_id; + value = (dcerpc_call_value *)wmem_map_lookup(dcerpc_matched, &matched_key); + if (!value) { + dcerpc_cn_call_key call_key; + dcerpc_call_value *call_value; + + call_key.conv = conv; + call_key.call_id = hdr->call_id; + call_key.transport_salt = dcerpc_get_transport_salt(pinfo); + + if ((call_value = (dcerpc_call_value *)wmem_map_lookup(dcerpc_cn_calls, &call_key))) { + new_matched_key = wmem_new(wmem_file_scope(), dcerpc_matched_key); + *new_matched_key = matched_key; + wmem_map_insert(dcerpc_matched, new_matched_key, call_value); + + value = call_value; + if (call_value->rep_frame == 0) { + call_value->rep_frame = pinfo->num; + } + + } + } + + if (value) { + proto_tree *stub_tree = NULL; + gint stub_length; + dcerpc_info *di; + proto_item *parent_pi; + + di = wmem_new0(pinfo->pool, dcerpc_info); + /* handoff this call */ + di->dcerpc_procedure_name = ""; + di->conv = conv; + di->call_id = hdr->call_id; + di->transport_salt = dcerpc_get_transport_salt(pinfo); + di->ptype = PDU_FAULT; + di->call_data = value; + + pi = proto_tree_add_uint(dcerpc_tree, hf_dcerpc_opnum, tvb, 0, 0, value->opnum); + proto_item_set_generated(pi); + if (value->req_frame != 0) { + nstime_t delta_ts; + pi = proto_tree_add_uint(dcerpc_tree, hf_dcerpc_request_in, + tvb, 0, 0, value->req_frame); + proto_item_set_generated(pi); + parent_pi = proto_tree_get_parent(dcerpc_tree); + if (parent_pi != NULL) { + proto_item_append_text(parent_pi, ", [Req: #%u]", value->req_frame); + } + nstime_delta(&delta_ts, &pinfo->abs_ts, &value->req_time); + pi = proto_tree_add_time(dcerpc_tree, hf_dcerpc_time, tvb, offset, 0, &delta_ts); + proto_item_set_generated(pi); + } else { + proto_tree_add_expert(dcerpc_tree, pinfo, &ei_dcerpc_no_request_found, tvb, 0, 0); + } + + length = tvb_reported_length_remaining(stub_tvb, 0); + /* as we now create a tvb in dissect_dcerpc_cn() containing only the + * stub_data, the following calculation is no longer valid: + * stub_length = hdr->frag_len - offset - auth_info.auth_size; + * simply use the remaining length of the tvb instead. + * XXX - or better use the reported_length?!? + */ + stub_length = length; + + stub_tree = proto_tree_add_subtree_format(dcerpc_tree, + stub_tvb, 0, stub_length, + ett_dcerpc_fault_stub_data, NULL, + "Fault stub data (%d byte%s)", stub_length, + plurality(stub_length, "", "s")); + + /* If we don't have reassembly enabled, or this packet contains + the entire PDU, or if we don't have all the data in this + fragment, just call the handoff directly if this is the + first fragment or the PDU isn't fragmented. */ + if ( (!dcerpc_reassemble) || PFC_NOT_FRAGMENTED(hdr) || + !tvb_bytes_exist(stub_tvb, 0, stub_length) ) { + if (hdr->flags&PFC_FIRST_FRAG) { + /* First fragment, possibly the only fragment */ + /* + * XXX - should there be a third routine for each + * function in an RPC subdissector, to handle + * fault responses? The DCE RPC 1.1 spec says + * three's "stub data" here, which I infer means + * that it's protocol-specific and call-specific. + * + * It should probably get passed the status code + * as well, as that might be protocol-specific. + */ + if (stub_length > 0) { + proto_tree_add_item(stub_tree, hf_dcerpc_fault_stub_data, stub_tvb, 0, stub_length, ENC_NA); + } + } else { + /* PDU is fragmented and this isn't the first fragment */ + if (stub_length > 0) { + proto_tree_add_item(stub_tree, hf_dcerpc_fragment_data, stub_tvb, 0, stub_length, ENC_NA); + } + } + } else { + /* Reassembly is enabled, the PDU is fragmented, and + we have all the data in the fragment; the first two + of those mean we should attempt reassembly, and the + third means we can attempt reassembly. */ + if (dcerpc_tree) { + if (length > 0) { + proto_tree_add_item(stub_tree, hf_dcerpc_fragment_data, stub_tvb, 0, stub_length, ENC_NA); + } + } + if (hdr->flags&PFC_FIRST_FRAG) { /* FIRST fragment */ + if ( (!pinfo->fd->visited) && value->rep_frame ) { + fragment_add_seq_next(&dcerpc_co_reassembly_table, + stub_tvb, 0, + pinfo, value->rep_frame, NULL, + stub_length, + TRUE); + } + } else if (hdr->flags&PFC_LAST_FRAG) { /* LAST fragment */ + if ( value->rep_frame ) { + fragment_head *fd_head; + + fd_head = fragment_add_seq_next(&dcerpc_co_reassembly_table, + stub_tvb, 0, + pinfo, value->rep_frame, NULL, + stub_length, + TRUE); + + if (fd_head) { + /* We completed reassembly */ + tvbuff_t *next_tvb; + proto_item *frag_tree_item; + + next_tvb = tvb_new_chain(stub_tvb, fd_head->tvb_data); + add_new_data_source(pinfo, next_tvb, "Reassembled DCE/RPC"); + show_fragment_tree(fd_head, &dcerpc_frag_items, + dcerpc_tree, pinfo, next_tvb, &frag_tree_item); + + /* + * XXX - should there be a third routine for each + * function in an RPC subdissector, to handle + * fault responses? The DCE RPC 1.1 spec says + * three's "stub data" here, which I infer means + * that it's protocol-specific and call-specific. + * + * It should probably get passed the status code + * as well, as that might be protocol-specific. + */ + if (dcerpc_tree) { + if (length > 0) { + proto_tree_add_item(dcerpc_tree, hf_dcerpc_stub_data, stub_tvb, 0, stub_length, ENC_NA); + } + } + } + } + } else { /* MIDDLE fragment(s) */ + if ( (!pinfo->fd->visited) && value->rep_frame ) { + fragment_add_seq_next(&dcerpc_co_reassembly_table, + stub_tvb, 0, + pinfo, value->rep_frame, NULL, + stub_length, + TRUE); + } + } + } + } + } + + /* + * Move the auth_info subtree to the end, + * as it's also at the end of the pdu on the wire. + */ + dissect_dcerpc_cn_auth_move(&auth_info, dcerpc_tree); +} + +static void +dissect_dcerpc_cn_rts(tvbuff_t *tvb, gint offset, packet_info *pinfo, + proto_tree *dcerpc_tree, e_dce_cn_common_hdr_t *hdr) +{ + proto_item *tf = NULL; + proto_item *parent_pi = NULL; + proto_tree *cn_rts_pdu_tree = NULL; + guint16 rts_flags; + guint16 commands_nb = 0; + guint32 *cmd; + guint32 i; + const char *info_str = NULL; + static int * const flags[] = { + &hf_dcerpc_cn_rts_flags_ping, + &hf_dcerpc_cn_rts_flags_other_cmd, + &hf_dcerpc_cn_rts_flags_recycle_channel, + &hf_dcerpc_cn_rts_flags_in_channel, + &hf_dcerpc_cn_rts_flags_out_channel, + &hf_dcerpc_cn_rts_flags_eof, + NULL + }; + + /* Dissect specific RTS header */ + rts_flags = dcerpc_tvb_get_ntohs(tvb, offset, hdr->drep); + proto_tree_add_bitmask_value_with_flags(dcerpc_tree, tvb, offset, hf_dcerpc_cn_rts_flags, + ett_dcerpc_cn_rts_flags, flags, rts_flags, BMT_NO_APPEND); + offset += 2; + + offset = dissect_dcerpc_uint16(tvb, offset, pinfo, dcerpc_tree, hdr->drep, + hf_dcerpc_cn_rts_commands_nb, &commands_nb); + + /* Create the RTS PDU tree - we do not yet know its name */ + cn_rts_pdu_tree = proto_tree_add_subtree_format(dcerpc_tree, tvb, offset, -1, ett_dcerpc_cn_rts_pdu, &tf, "RTS PDU: %u commands", commands_nb); + + cmd = (guint32 *)wmem_alloc(pinfo->pool, sizeof (guint32) * (commands_nb + 1)); + + /* Dissect commands */ + for (i = 0; i < commands_nb; ++i) { + proto_tree *cn_rts_command_tree = NULL; + const guint32 command = dcerpc_tvb_get_ntohl(tvb, offset, hdr->drep); + cmd[i] = command; + tf = proto_tree_add_uint(cn_rts_pdu_tree, hf_dcerpc_cn_rts_command, tvb, offset, 4, command); + cn_rts_command_tree = proto_item_add_subtree(tf, ett_dcerpc_cn_rts_command); + offset += 4; + switch (command) { + case RTS_CMD_RECEIVEWINDOWSIZE: + offset = dissect_dcerpc_uint32(tvb, offset, pinfo, cn_rts_command_tree, hdr->drep, hf_dcerpc_cn_rts_command_receivewindowsize, NULL); + break; + case RTS_CMD_FLOWCONTROLACK: + offset = dissect_dcerpc_uint32(tvb, offset, pinfo, cn_rts_command_tree, hdr->drep, hf_dcerpc_cn_rts_command_fack_bytesreceived, NULL); + offset = dissect_dcerpc_uint32(tvb, offset, pinfo, cn_rts_command_tree, hdr->drep, hf_dcerpc_cn_rts_command_fack_availablewindow, NULL); + offset = dissect_dcerpc_uuid_t(tvb, offset, pinfo, cn_rts_command_tree, hdr->drep, hf_dcerpc_cn_rts_command_fack_channelcookie, NULL); + break; + case RTS_CMD_CONNECTIONTIMEOUT: + offset = dissect_dcerpc_uint32(tvb, offset, pinfo, cn_rts_command_tree, hdr->drep, hf_dcerpc_cn_rts_command_connectiontimeout, NULL); + break; + case RTS_CMD_COOKIE: + offset = dissect_dcerpc_uuid_t(tvb, offset, pinfo, cn_rts_command_tree, hdr->drep, hf_dcerpc_cn_rts_command_cookie, NULL); + break; + case RTS_CMD_CHANNELLIFETIME: + offset = dissect_dcerpc_uint32(tvb, offset, pinfo, cn_rts_command_tree, hdr->drep, hf_dcerpc_cn_rts_command_channellifetime, NULL); + break; + case RTS_CMD_CLIENTKEEPALIVE: + offset = dissect_dcerpc_uint32(tvb, offset, pinfo, cn_rts_command_tree, hdr->drep, hf_dcerpc_cn_rts_command_clientkeepalive, NULL); + break; + case RTS_CMD_VERSION: + offset = dissect_dcerpc_uint32(tvb, offset, pinfo, cn_rts_command_tree, hdr->drep, hf_dcerpc_cn_rts_command_version, NULL); + break; + case RTS_CMD_EMPTY: + break; + case RTS_CMD_PADDING: { + guint8 *padding; + const guint32 conformance_count = dcerpc_tvb_get_ntohl(tvb, offset, hdr->drep); + proto_tree_add_uint(cn_rts_command_tree, hf_dcerpc_cn_rts_command_conformancecount, tvb, offset, 4, conformance_count); + offset += 4; + padding = (guint8 *)tvb_memdup(pinfo->pool, tvb, offset, conformance_count); + proto_tree_add_bytes(cn_rts_command_tree, hf_dcerpc_cn_rts_command_padding, tvb, offset, conformance_count, padding); + offset += conformance_count; + } break; + case RTS_CMD_NEGATIVEANCE: + break; + case RTS_CMD_ANCE: + break; + case RTS_CMD_CLIENTADDRESS: { + guint8 *padding; + const guint32 addrtype = dcerpc_tvb_get_ntohl(tvb, offset, hdr->drep); + proto_tree_add_uint(cn_rts_command_tree, hf_dcerpc_cn_rts_command_addrtype, tvb, offset, 4, addrtype); + offset += 4; + switch (addrtype) { + case RTS_IPV4: { + const guint32 addr4 = tvb_get_ipv4(tvb, offset); + proto_tree_add_ipv4_format_value(cn_rts_command_tree, hf_dcerpc_cmd_client_ipv4, tvb, offset, 4, addr4, "%s", get_hostname(addr4)); + offset += 4; + } break; + case RTS_IPV6: { + ws_in6_addr addr6; + tvb_get_ipv6(tvb, offset, &addr6); + proto_tree_add_ipv6_format_value(cn_rts_command_tree, hf_dcerpc_cmd_client_ipv6, tvb, offset, 16, &addr6, "%s", get_hostname6(&addr6)); + offset += 16; + } break; + } + padding = (guint8 *)tvb_memdup(pinfo->pool, tvb, offset, 12); + proto_tree_add_bytes(cn_rts_command_tree, hf_dcerpc_cn_rts_command_padding, tvb, offset, 12, padding); + offset += 12; + } break; + case RTS_CMD_ASSOCIATIONGROUPID: + offset = dissect_dcerpc_uuid_t(tvb, offset, pinfo, cn_rts_command_tree, hdr->drep, hf_dcerpc_cn_rts_command_associationgroupid, NULL); + break; + case RTS_CMD_DESTINATION: + offset = dissect_dcerpc_uint32(tvb, offset, pinfo, cn_rts_command_tree, hdr->drep, hf_dcerpc_cn_rts_command_forwarddestination, NULL); + break; + case RTS_CMD_PINGTRAFFICSENTNOTIFY: + offset = dissect_dcerpc_uint32(tvb, offset, pinfo, cn_rts_command_tree, hdr->drep, hf_dcerpc_cn_rts_command_pingtrafficsentnotify, NULL); + break; + default: + expert_add_info(pinfo, tf, &ei_dcerpc_cn_rts_command); + break; + } + } + + col_set_str(pinfo->cinfo, COL_PROTOCOL, "RPCH"); + + /* Define which PDU Body we are dealing with */ + info_str = "unknown RTS PDU"; + + switch (rts_flags) { + case RTS_FLAG_NONE: + switch (commands_nb) { + case 1: + if (cmd[0] == 0x2) { + info_str = "CONN/A3"; + } else if (cmd[0] == 0x3) { + info_str = "IN_R1/A5,IN_R1/A6,IN_R2/A2,IN_R2/A5,OUT_R2/A4"; + } else if (cmd[0] == 0x7) { + info_str = "IN_R1/B1"; + } else if (cmd[0] == 0x0) { + info_str = "IN_R1/B2"; + } else if (cmd[0] == 0xD) { + info_str = "IN_R2/A3,IN_R2/A4"; + } else if (cmd[0] == 0xA) { + info_str = "OUT_R1/A9,OUT_R1/A10,OUT_R1/A11,OUT_R2/B1,OUT_R2/B2"; + } + break; + case 2: + if ((cmd[0] == 0x0) && (cmd[1] == 0x6)) { + info_str = "CONN/B3"; + } else if ((cmd[0] == 0xD) && (cmd[1] == 0xA)) { + info_str = "OUT_R2/A5,OUT_R2/A6"; + } + break; + case 3: + if ((cmd[0] == 0x6) && (cmd[1] == 0x0) && (cmd[2] == 0x2)) { + info_str = "CONN/C1,CONN/C2"; + } + break; + case 4: + if ((cmd[0] == 0x6) && (cmd[1] == 0x3) && (cmd[2] == 0x3) && (cmd[3] == 0x0)) { + info_str = "CONN/A1"; + } else if ((cmd[0] == 0xD) && (cmd[1] == 0x6) && (cmd[2] == 0x0) && (cmd[3] == 0x2)) { + info_str = "IN_R1/A3,IN_R1/A4"; + } + break; + case 6: + if ((cmd[0] == 0x6) && (cmd[1] == 0x3) && (cmd[2] == 0x3) && (cmd[3] == 0x4) && (cmd[4] == 0x5) && (cmd[5] == 0xC)) { + info_str = "CONN/B1"; + } + break; + default: + break; + } + break; + case RTS_FLAG_PING: + switch (commands_nb) { + case 0: + info_str = "Ping"; + break; + case 1: + if ((cmd[0] == 0x7) || (cmd[0] == 0x8)) { + info_str = "OUT_R2/C1"; + } + break; + default: + break; + } + break; + case RTS_FLAG_OTHER_CMD: + switch (commands_nb) { + case 1: + if (cmd[0] == 0x5) { + info_str = "Keep-Alive"; + } else if (cmd[0] == 0xE) { + info_str = "PingTrafficSentNotify"; + } else if (cmd[0] == 0x1) { + info_str = "FlowControlAck"; + } + break; + case 2: + if ((cmd[0] == 0xD) && (cmd[1] == 0x1)) { + info_str = "FlowControlAckWithDestination"; + } + break; + default: + break; + } + break; + case RTS_FLAG_RECYCLE_CHANNEL: + switch (commands_nb) { + case 1: + if (cmd[0] == 0xD) { + info_str = "OUT_R1/A1,OUT_R1/A2,OUT_R2/A1,OUT_R2/A2"; + } + break; + case 4: + if ((cmd[0] == 0x6) && (cmd[1] == 0x3) && (cmd[2] == 0x3) && (cmd[3] == 0x3)) { + info_str = "IN_R1/A1,IN_R2/A1"; + } + break; + case 5: + if ((cmd[0] == 0x6) && (cmd[1] == 0x3) && (cmd[2] == 0x3) && (cmd[3] == 0x3) && (cmd[4] == 0x0)) { + info_str = "OUT_R1/A3,OUT_R2/A3"; + } + break; + default: + break; + } + break; + case RTS_FLAG_IN_CHANNEL|RTS_FLAG_RECYCLE_CHANNEL: + switch (commands_nb) { + case 6: + if ((cmd[0] == 0x6) && (cmd[1] == 0x3) && (cmd[2] == 0x3) && (cmd[3] == 0x3) && (cmd[4] == 0x0) && (cmd[5] == 0x2)) { + info_str = "IN_R1/A2"; + } + break; + default: + break; + } + break; + case RTS_FLAG_IN_CHANNEL: + switch (commands_nb) { + case 7: + if ((cmd[0] == 0x6) && (cmd[1] == 0x3) && (cmd[2] == 0x3) && (cmd[3] == 0x0) && (cmd[4] == 0x2) && (cmd[5] == 0xC) && (cmd[6] == 0xB)) { + info_str = "CONN/B2"; + } + break; + default: + break; + } + break; + case RTS_FLAG_OUT_CHANNEL|RTS_FLAG_RECYCLE_CHANNEL: + switch (commands_nb) { + case 7: + if ((cmd[0] == 0x6) && (cmd[1] == 0x3) && (cmd[2] == 0x3) && (cmd[3] == 0x3) && (cmd[4] == 0x4) && (cmd[5] == 0) && (cmd[6] == 0x2)) { + info_str = "OUT_R1/A4"; + } + break; + default: + break; + } + break; + case RTS_FLAG_OUT_CHANNEL: + switch (commands_nb) { + case 2: + if ((cmd[0] == 0xD) && (cmd[1] == 0x3)) { + info_str = "OUT_R1/A7,OUT_R1/A8,OUT_R2/A8"; + } + break; + case 3: + if ((cmd[0] == 0xD) && (cmd[1] == 0x6) && (cmd[2] == 0x2)) { + info_str = "OUT_R1/A5,OUT_R1/A6"; + } else if ((cmd[0] == 0xD) && (cmd[1] == 0x3) && (cmd[2] == 0x6)) { + info_str = "OUT_R2/A7"; + } + break; + case 5: + if ((cmd[0] == 0x6) && (cmd[1] == 0x3) && (cmd[2] == 0x3) && (cmd[3] == 0x4) && (cmd[4] == 0x0)) { + info_str = "CONN/A2"; + } + break; + default: + break; + } + break; + case RTS_FLAG_EOF: + switch (commands_nb) { + case 1: + if (cmd[0] == 0xA) { + info_str = "OUT_R2/B3"; + } + break; + default: + break; + } + break; + case RTS_FLAG_ECHO: + switch (commands_nb) { + case 0: + info_str = "Echo"; + break; + default: + break; + } + break; + default: + break; + } + + col_add_fstr(pinfo->cinfo, COL_INFO, "%s, ", info_str); + col_set_fence(pinfo->cinfo,COL_INFO); + + parent_pi = proto_tree_get_parent(dcerpc_tree); + if (parent_pi != NULL) { + proto_item_append_text(parent_pi, ", %s", info_str); + } +} + +/* Test to see if this looks like a connection oriented PDU */ +static gboolean +is_dcerpc(tvbuff_t *tvb, int offset, packet_info *pinfo _U_) +{ + guint8 rpc_ver; + guint8 rpc_ver_minor; + guint8 ptype; + guint8 drep[4]; + guint16 frag_len; + + if (!tvb_bytes_exist(tvb, offset, sizeof(e_dce_cn_common_hdr_t))) + return FALSE; /* not enough information to check */ + + rpc_ver = tvb_get_guint8(tvb, offset++); + if (rpc_ver != 5) + return FALSE; + rpc_ver_minor = tvb_get_guint8(tvb, offset++); + if ((rpc_ver_minor != 0) && (rpc_ver_minor != 1)) + return FALSE; + ptype = tvb_get_guint8(tvb, offset++); + if (ptype > PDU_RTS) + return FALSE; + /* Skip flags, nothing good to check */ + offset++; + + tvb_memcpy(tvb, (guint8 *)drep, offset, sizeof (drep)); + if (drep[0]&0xee) + return FALSE; + if (drep[1] > DCE_RPC_DREP_FP_IBM) + return FALSE; + offset += (int)sizeof(drep); + frag_len = dcerpc_tvb_get_ntohs(tvb, offset, drep); + if (frag_len < sizeof(e_dce_cn_common_hdr_t)) { + return FALSE; + } + + return TRUE; +} + +/* + * DCERPC dissector for connection oriented calls. + */ +static gboolean +dissect_dcerpc_cn(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *tree, gboolean can_desegment, int *pkt_len) +{ + static const guint8 nulls[4] = { 0 }; + int start_offset; + int padding = 0; + int subtvb_len = 0; + proto_item *ti = NULL; + proto_item *tf = NULL; + proto_tree *dcerpc_tree = NULL; + e_dce_cn_common_hdr_t hdr; + dcerpc_auth_info auth_info; + tvbuff_t *fragment_tvb; + dcerpc_decode_as_data* decode_data = dcerpc_get_decode_data(pinfo); + static int * const hdr_flags[] = { + &hf_dcerpc_cn_flags_object, + &hf_dcerpc_cn_flags_maybe, + &hf_dcerpc_cn_flags_dne, + &hf_dcerpc_cn_flags_mpx, + &hf_dcerpc_cn_flags_reserved, + &hf_dcerpc_cn_flags_cancel_pending, + &hf_dcerpc_cn_flags_last_frag, + &hf_dcerpc_cn_flags_first_frag, + NULL + }; + + /* + * when done over nbt, dcerpc requests are padded with 4 bytes of null + * data for some reason. + * + * XXX - if that's always the case, the right way to do this would + * be to have a "dissect_dcerpc_cn_nb" routine which strips off + * the 4 bytes of null padding, and make that the dissector + * used for "netbios". + */ + if (tvb_memeql(tvb, offset, nulls, 4) == 0) { + + /* + * Skip the padding. + */ + offset += 4; + padding += 4; + } + /* + * Check if this looks like a C/O DCERPC call + */ + if (!is_dcerpc(tvb, offset, pinfo)) + return FALSE; + + start_offset = offset; + hdr.rpc_ver = tvb_get_guint8(tvb, offset++); + hdr.rpc_ver_minor = tvb_get_guint8(tvb, offset++); + hdr.ptype = tvb_get_guint8(tvb, offset++); + + hdr.flags = tvb_get_guint8(tvb, offset++); + tvb_memcpy(tvb, (guint8 *)hdr.drep, offset, sizeof (hdr.drep)); + offset += (int)sizeof (hdr.drep); + + hdr.frag_len = dcerpc_tvb_get_ntohs(tvb, offset, hdr.drep); + offset += 2; + hdr.auth_len = dcerpc_tvb_get_ntohs(tvb, offset, hdr.drep); + offset += 2; + hdr.call_id = dcerpc_tvb_get_ntohl(tvb, offset, hdr.drep); + /*offset += 4;*/ + + if (can_desegment && pinfo->can_desegment + && !tvb_bytes_exist(tvb, start_offset, hdr.frag_len)) { + pinfo->desegment_offset = start_offset; + pinfo->desegment_len = hdr.frag_len - tvb_reported_length_remaining(tvb, start_offset); + *pkt_len = 0; /* desegmentation required */ + return TRUE; + } + + col_set_str(pinfo->cinfo, COL_PROTOCOL, "DCERPC"); + + if (decode_data->dcectxid != 0) { + /* this is not the first DCE-RPC request/response in this (TCP?-)PDU, + * append a delimiter and set a column fence */ + col_append_str(pinfo->cinfo, COL_INFO, " # "); + col_set_fence(pinfo->cinfo,COL_INFO); + } + col_add_fstr(pinfo->cinfo, COL_INFO, "%s: call_id: %u", + pckt_vals[hdr.ptype].strptr, hdr.call_id); + + if (decode_data->dcectxid != 0) { + /* this is not the first DCE-RPC request/response in this (TCP?-)PDU */ + expert_add_info(pinfo, NULL, &ei_dcerpc_fragment_multiple); + } + + offset = start_offset; + tvb_ensure_bytes_exist(tvb, offset, 16); + if (tree) { + ti = proto_tree_add_item(tree, proto_dcerpc, tvb, offset, hdr.frag_len, ENC_NA); + dcerpc_tree = proto_item_add_subtree(ti, ett_dcerpc); + } + + proto_tree_add_uint(dcerpc_tree, hf_dcerpc_ver, tvb, offset, 1, hdr.rpc_ver); + offset++; + + proto_tree_add_uint(dcerpc_tree, hf_dcerpc_ver_minor, tvb, offset, 1, hdr.rpc_ver_minor); + offset++; + + tf = proto_tree_add_uint(dcerpc_tree, hf_dcerpc_packet_type, tvb, offset, 1, hdr.ptype); + offset++; + +#if 0 /* XXX - too much "output noise", removed for now */ + if (hdr.ptype == PDU_BIND || hdr.ptype == PDU_ALTER || + hdr.ptype == PDU_BIND_ACK || hdr.ptype == PDU_ALTER_ACK) + expert_add_info_format(pinfo, tf, &ei_dcerpc_context_change, "Context change: %s", val_to_str(hdr.ptype, pckt_vals, "(0x%x)")); +#endif + if (hdr.ptype == PDU_BIND_NAK) + expert_add_info(pinfo, tf, &ei_dcerpc_bind_not_acknowledged); + + if (tree) { + proto_item_append_text(ti, " %s, Fragment: %s", + val_to_str(hdr.ptype, pckt_vals, "Unknown (0x%02x)"), + fragment_type(hdr.flags)); + } + + proto_tree_add_bitmask_value_with_flags(dcerpc_tree, tvb, offset, hf_dcerpc_cn_flags, + ett_dcerpc_cn_flags, hdr_flags, hdr.flags, BMT_NO_APPEND); + offset++; + + col_append_fstr(pinfo->cinfo, COL_INFO, ", Fragment: %s", fragment_type(hdr.flags)); + + proto_tree_add_dcerpc_drep(dcerpc_tree, tvb, offset, hdr.drep, (int)sizeof (hdr.drep)); + offset += (int)sizeof (hdr.drep); + + proto_tree_add_uint(dcerpc_tree, hf_dcerpc_cn_frag_len, tvb, offset, 2, hdr.frag_len); + offset += 2; + + proto_tree_add_uint(dcerpc_tree, hf_dcerpc_cn_auth_len, tvb, offset, 2, hdr.auth_len); + offset += 2; + + proto_tree_add_uint(dcerpc_tree, hf_dcerpc_cn_call_id, tvb, offset, 4, hdr.call_id); + offset += 4; + + if (ti) { + proto_item_append_text(ti, ", FragLen: %u, Call: %u", hdr.frag_len, hdr.call_id); + } + + /* + * None of the stuff done above should throw an exception, because + * we would have rejected this as "not DCE RPC" if we didn't have all + * of it. (XXX - perhaps we should request reassembly if we have + * enough of the header to consider it DCE RPC but not enough to + * get the fragment length; in that case the stuff still wouldn't + * throw an exception.) + * + * The rest of the stuff might, so return the PDU length to our caller. + * XXX - should we construct a tvbuff containing only the PDU and + * use that? Or should we have separate "is this a DCE RPC PDU", + * "how long is it", and "dissect it" routines - which might let us + * do most of the work in "tcp_dissect_pdus()"? + */ + if (pkt_len != NULL) + *pkt_len = hdr.frag_len + padding; + + /* The remaining bytes in the current tvb might contain multiple + * DCE/RPC fragments, so create a new tvb subset for this fragment. + * Only limit the end of the fragment, but not the offset start, + * as the authentication function dissect_dcerpc_cn_auth() will fail + * (and other functions might fail as well) computing the right start + * offset otherwise. + */ + subtvb_len = MIN(hdr.frag_len, tvb_reported_length(tvb)); + fragment_tvb = tvb_new_subset_length_caplen(tvb, start_offset, + subtvb_len /* length */, + hdr.frag_len /* reported_length */); + + /* + * Packet type specific stuff is next. + */ + switch (hdr.ptype) { + case PDU_BIND: + case PDU_ALTER: + dissect_dcerpc_cn_bind(fragment_tvb, MIN(offset - start_offset, subtvb_len), pinfo, dcerpc_tree, &hdr); + break; + + case PDU_BIND_ACK: + case PDU_ALTER_ACK: + dissect_dcerpc_cn_bind_ack(fragment_tvb, MIN(offset - start_offset, subtvb_len), pinfo, dcerpc_tree, &hdr); + break; + + case PDU_AUTH3: + /* + * Nothing after the common header other than credentials. + */ + dissect_dcerpc_cn_auth(fragment_tvb, MIN(offset - start_offset, subtvb_len), pinfo, dcerpc_tree, &hdr, + &auth_info); + break; + + case PDU_REQ: + dissect_dcerpc_cn_rqst(fragment_tvb, MIN(offset - start_offset, subtvb_len), pinfo, dcerpc_tree, tree, &hdr); + break; + + case PDU_RESP: + dissect_dcerpc_cn_resp(fragment_tvb, MIN(offset - start_offset, subtvb_len), pinfo, dcerpc_tree, tree, &hdr); + break; + + case PDU_FAULT: + dissect_dcerpc_cn_fault(fragment_tvb, MIN(offset - start_offset, subtvb_len), pinfo, dcerpc_tree, &hdr); + break; + + case PDU_BIND_NAK: + dissect_dcerpc_cn_bind_nak(fragment_tvb, MIN(offset - start_offset, subtvb_len), pinfo, dcerpc_tree, &hdr); + break; + + case PDU_CO_CANCEL: + case PDU_ORPHANED: + /* + * Nothing after the common header other than an authentication + * verifier. + */ + dissect_dcerpc_cn_auth(fragment_tvb, MIN(offset - start_offset, subtvb_len), pinfo, dcerpc_tree, &hdr, + &auth_info); + break; + + case PDU_SHUTDOWN: + /* + * Nothing after the common header, not even an authentication + * verifier. + */ + break; + case PDU_RTS: + dissect_dcerpc_cn_rts(fragment_tvb, MIN(offset - start_offset, subtvb_len), pinfo, dcerpc_tree, &hdr); + break; + + default: + /* might as well dissect the auth info */ + dissect_dcerpc_cn_auth(fragment_tvb, MIN(offset - start_offset, subtvb_len), pinfo, dcerpc_tree, &hdr, + &auth_info); + break; + } + return TRUE; +} + +/* + * DCERPC dissector for connection oriented calls over packet-oriented + * transports + */ +static gboolean +dissect_dcerpc_cn_pk(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + dcerpc_decode_as_data* decode_data = dcerpc_get_decode_data(pinfo); + + /* + * Only one PDU per transport packet, and only one transport + * packet per PDU. + */ + decode_data->dcetransporttype = DCE_TRANSPORT_UNKNOWN; + if (!dissect_dcerpc_cn(tvb, 0, pinfo, tree, FALSE, NULL)) { + /* + * It wasn't a DCERPC PDU. + */ + return FALSE; + } else { + /* + * It was. + */ + return TRUE; + } +} + +/* + * DCERPC dissector for connection oriented calls over byte-stream + * transports. + * we need to distinguish here between SMB and non-TCP (more in the future?) + * to be able to know what kind of private_data structure to expect. + */ +static gboolean +dissect_dcerpc_cn_bs_body(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + volatile int offset = 0; + int pdu_len = 0; + volatile int dcerpc_pdus = 0; + volatile gboolean ret = FALSE; + + /* + * There may be multiple PDUs per transport packet; keep + * processing them. + */ + while (tvb_reported_length_remaining(tvb, offset) != 0) { + TRY { + pdu_len = 0; + if (dissect_dcerpc_cn(tvb, offset, pinfo, tree, + dcerpc_cn_desegment, &pdu_len)) { + dcerpc_pdus++; + } + } CATCH_NONFATAL_ERRORS { + /* + * Somebody threw an exception that means that there + * was a problem dissecting the payload; that means + * that a dissector was found, so we don't need to + * dissect the payload as data or update the protocol + * or info columns. + * + * Just show the exception and then continue dissecting + * PDUs. + */ + show_exception(tvb, pinfo, tree, EXCEPT_CODE, GET_MESSAGE); + /* + * Presumably it looked enough like a DCE RPC PDU that we + * dissected enough of it to throw an exception. + */ + dcerpc_pdus++; + } ENDTRY; + + if (dcerpc_pdus == 0) { + gboolean try_desegment = FALSE; + if (dcerpc_cn_desegment && pinfo->can_desegment && + !tvb_bytes_exist(tvb, offset, sizeof(e_dce_cn_common_hdr_t))) { + /* look for a previous occurrence of the DCE-RPC protocol */ + wmem_list_frame_t *cur; + cur = wmem_list_frame_prev(wmem_list_tail(pinfo->layers)); + while (cur != NULL) { + if (proto_dcerpc == (gint)GPOINTER_TO_UINT(wmem_list_frame_data(cur))) { + try_desegment = TRUE; + break; + } + cur = wmem_list_frame_prev(cur); + } + } + + if (try_desegment) { + /* It didn't look like DCE-RPC but we already had one DCE-RPC + * layer in this packet and what we have is short. Assume that + * it was just too short to tell and ask the TCP layer for more + * data. */ + pinfo->desegment_offset = offset; + pinfo->desegment_len = (guint32)(sizeof(e_dce_cn_common_hdr_t) - tvb_reported_length_remaining(tvb, offset)); + } else { + /* Really not DCE-RPC */ + break; + } + } + + /* + * Well, we've seen at least one DCERPC PDU. + */ + ret = TRUE; + + /* if we had more than one Req/Resp in this PDU change the protocol column */ + /* this will formerly contain the last interface name, which may not be the same for all Req/Resp */ + if (dcerpc_pdus >= 2) + col_add_fstr(pinfo->cinfo, COL_PROTOCOL, "%u*DCERPC", dcerpc_pdus); + + if (pdu_len == 0) { + /* + * Desegmentation required - bail now, but give the user a hint that desegmentation might be done later. + */ + proto_tree_add_uint_format(tree, hf_dcerpc_cn_deseg_req, tvb, offset, + 0, + tvb_reported_length_remaining(tvb, offset), + "[DCE RPC: %u byte%s left, desegmentation might follow]", + tvb_reported_length_remaining(tvb, offset), + plurality(tvb_reported_length_remaining(tvb, offset), "", "s")); + break; + } + + /* + * Step to the next PDU. + */ + offset += pdu_len; + } + return ret; +} + +static gboolean +dissect_dcerpc_cn_bs(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + dcerpc_decode_as_data* decode_data = dcerpc_get_decode_data(pinfo); + + decode_data->dcetransporttype = DCE_TRANSPORT_UNKNOWN; + return dissect_dcerpc_cn_bs_body(tvb, pinfo, tree); +} + +static guint +get_dcerpc_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, + int offset, void *data _U_) +{ + guint8 drep[4]; + guint16 frag_len; + + tvb_memcpy(tvb, (guint8 *)drep, offset+4, sizeof(drep)); + frag_len = dcerpc_tvb_get_ntohs(tvb, offset+8, drep); + + if (!frag_len) { + /* tcp_dissect_pdus() interprets a 0 return value as meaning + * "a PDU starts here, but the length cannot be determined yet, so + * we need at least one more segment." However, a frag_len of 0 here + * is instead a bogus length. Instead return 1, another bogus length + * also less than our fixed length, so that the TCP dissector will + * correctly interpret it as a bogus and report an error. + */ + frag_len = 1; + } + return frag_len; +} + +static int +dissect_dcerpc_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) +{ + int pdu_len = 0; + dissect_dcerpc_cn(tvb, 0, pinfo, tree, + /* Desegment is already handled by TCP, don't confuse it */ + FALSE, + &pdu_len); + return pdu_len; +} + +static gboolean +dissect_dcerpc_tcp_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) +{ + dcerpc_decode_as_data* decode_data; + + if (!is_dcerpc(tvb, 0, pinfo)) + return 0; + + decode_data = dcerpc_get_decode_data(pinfo); + decode_data->dcetransporttype = DCE_TRANSPORT_UNKNOWN; + + tcp_dissect_pdus(tvb, pinfo, tree, dcerpc_cn_desegment, sizeof(e_dce_cn_common_hdr_t), get_dcerpc_pdu_len, dissect_dcerpc_pdu, data); + return TRUE; +} + +static int +dissect_dcerpc_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) +{ + dcerpc_decode_as_data* decode_data; + + decode_data = dcerpc_get_decode_data(pinfo); + decode_data->dcetransporttype = DCE_TRANSPORT_UNKNOWN; + + tcp_dissect_pdus(tvb, pinfo, tree, dcerpc_cn_desegment, sizeof(e_dce_cn_common_hdr_t), get_dcerpc_pdu_len, dissect_dcerpc_pdu, data); + return tvb_captured_length(tvb); +} + +static gboolean +dissect_dcerpc_cn_smbpipe(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + dcerpc_decode_as_data* decode_data = dcerpc_get_decode_data(pinfo); + + decode_data->dcetransporttype = DCE_CN_TRANSPORT_SMBPIPE; + return dissect_dcerpc_cn_bs_body(tvb, pinfo, tree); +} + +static gboolean +dissect_dcerpc_cn_smb2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + dcerpc_decode_as_data* decode_data = dcerpc_get_decode_data(pinfo); + + decode_data->dcetransporttype = DCE_CN_TRANSPORT_SMBPIPE; + return dissect_dcerpc_cn_bs_body(tvb, pinfo, tree); +} + + + +static void +dissect_dcerpc_dg_auth(tvbuff_t *tvb, int offset, proto_tree *dcerpc_tree, + e_dce_dg_common_hdr_t *hdr, int *auth_level_p) +{ + proto_tree *auth_tree = NULL; + guint8 protection_level; + + /* + * Initially set "*auth_level_p" to -1 to indicate that we haven't + * yet seen any authentication level information. + */ + if (auth_level_p != NULL) + *auth_level_p = -1; + + /* + * The authentication information is at the *end* of the PDU; in + * request and response PDUs, the request and response stub data + * come before it. + * + * If the full packet is here, and there's data past the end of the + * packet body, then dissect the auth info. + */ + offset += hdr->frag_len; + if (tvb_reported_length_remaining(tvb, offset) > 0) { + switch (hdr->auth_proto) { + + case DCE_C_RPC_AUTHN_PROTOCOL_KRB5: + auth_tree = proto_tree_add_subtree(dcerpc_tree, tvb, offset, -1, ett_dcerpc_krb5_auth_verf, NULL, "Kerberos authentication verifier"); + protection_level = tvb_get_guint8(tvb, offset); + if (auth_level_p != NULL) + *auth_level_p = protection_level; + proto_tree_add_uint(auth_tree, hf_dcerpc_krb5_av_prot_level, tvb, offset, 1, protection_level); + offset++; + proto_tree_add_item(auth_tree, hf_dcerpc_krb5_av_key_vers_num, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + if (protection_level == DCE_C_AUTHN_LEVEL_PKT_PRIVACY) + offset += 6; /* 6 bytes of padding */ + else + offset += 2; /* 2 bytes of padding */ + proto_tree_add_item(auth_tree, hf_dcerpc_krb5_av_key_auth_verifier, tvb, offset, 16, ENC_NA); + /*offset += 16;*/ + break; + + default: + proto_tree_add_item(dcerpc_tree, hf_dcerpc_authentication_verifier, tvb, offset, -1, ENC_NA); + break; + } + } +} + +static void +dissect_dcerpc_dg_cancel_ack(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *dcerpc_tree, + e_dce_dg_common_hdr_t *hdr) +{ + guint32 version; + + offset = dissect_dcerpc_uint32(tvb, offset, pinfo, dcerpc_tree, + hdr->drep, hf_dcerpc_dg_cancel_vers, + &version); + + switch (version) { + + case 0: + /* The only version we know about */ + offset = dissect_dcerpc_uint32(tvb, offset, pinfo, dcerpc_tree, + hdr->drep, hf_dcerpc_dg_cancel_id, + NULL); + /*offset = */dissect_dcerpc_uint8(tvb, offset, pinfo, dcerpc_tree, + hdr->drep, hf_dcerpc_dg_server_accepting_cancels, + NULL); + break; + } +} + +static void +dissect_dcerpc_dg_cancel(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *dcerpc_tree, + e_dce_dg_common_hdr_t *hdr) +{ + guint32 version; + + offset = dissect_dcerpc_uint32(tvb, offset, pinfo, dcerpc_tree, + hdr->drep, hf_dcerpc_dg_cancel_vers, + &version); + + switch (version) { + + case 0: + /* The only version we know about */ + /*offset = */dissect_dcerpc_uint32(tvb, offset, pinfo, dcerpc_tree, + hdr->drep, hf_dcerpc_dg_cancel_id, + NULL); + /* XXX - are NDR Booleans 32 bits? */ + + /* XXX - the RPC reference in chapter: "the cancel PDU" doesn't mention + the accepting_cancels field (it's only in the cancel_ack PDU)! */ + /*offset = dissect_dcerpc_uint32 (tvb, offset, pinfo, dcerpc_tree, + hdr->drep, hf_dcerpc_dg_server_accepting_cancels, + NULL);*/ + break; + } +} + +static void +dissect_dcerpc_dg_fack(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *dcerpc_tree, + e_dce_dg_common_hdr_t *hdr) +{ + guint8 version; + guint16 serial_num; + guint16 selack_len; + guint i; + + offset = dissect_dcerpc_uint8(tvb, offset, pinfo, dcerpc_tree, + hdr->drep, hf_dcerpc_dg_fack_vers, + &version); + /* padding */ + offset++; + + switch (version) { + + case 0: /* The only version documented in the DCE RPC 1.1 spec */ + case 1: /* This appears to be the same */ + offset = dissect_dcerpc_uint16(tvb, offset, pinfo, dcerpc_tree, + hdr->drep, hf_dcerpc_dg_fack_window_size, + NULL); + offset = dissect_dcerpc_uint32(tvb, offset, pinfo, dcerpc_tree, + hdr->drep, hf_dcerpc_dg_fack_max_tsdu, + NULL); + offset = dissect_dcerpc_uint32(tvb, offset, pinfo, dcerpc_tree, + hdr->drep, hf_dcerpc_dg_fack_max_frag_size, + NULL); + offset = dissect_dcerpc_uint16(tvb, offset, pinfo, dcerpc_tree, + hdr->drep, hf_dcerpc_dg_fack_serial_num, + &serial_num); + col_append_fstr(pinfo->cinfo, COL_INFO, " serial: %u", + serial_num); + offset = dissect_dcerpc_uint16(tvb, offset, pinfo, dcerpc_tree, + hdr->drep, hf_dcerpc_dg_fack_selack_len, + &selack_len); + for (i = 0; i < selack_len; i++) { + offset = dissect_dcerpc_uint32(tvb, offset, pinfo, dcerpc_tree, + hdr->drep, hf_dcerpc_dg_fack_selack, + NULL); + } + + break; + } +} + +static void +dissect_dcerpc_dg_reject_fault(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *dcerpc_tree, + e_dce_dg_common_hdr_t *hdr) +{ + guint32 status; + + /*offset = */dissect_dcerpc_uint32(tvb, offset, pinfo, dcerpc_tree, + hdr->drep, hf_dcerpc_dg_status, + &status); + + col_append_fstr (pinfo->cinfo, COL_INFO, + ": status: %s", + val_to_str(status, reject_status_vals, "Unknown (0x%08x)")); +} + +static void +dissect_dcerpc_dg_stub(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *dcerpc_tree, proto_tree *tree, + e_dce_dg_common_hdr_t *hdr, dcerpc_info *di) +{ + int length, reported_length, stub_length; + gboolean save_fragmented; + fragment_head *fd_head; + tvbuff_t *next_tvb; + proto_item *pi; + proto_item *parent_pi; + + col_append_fstr(pinfo->cinfo, COL_INFO, " opnum: %u len: %u", + di->call_data->opnum, hdr->frag_len ); + + length = tvb_reported_length_remaining(tvb, offset); + reported_length = tvb_reported_length_remaining(tvb, offset); + stub_length = hdr->frag_len; + if (length > stub_length) + length = stub_length; + if (reported_length > stub_length) + reported_length = stub_length; + + save_fragmented = pinfo->fragmented; + + /* If we don't have reassembly enabled, or this packet contains + the entire PDU, or if this is a short frame (or a frame + not reassembled at a lower layer) that doesn't include all + the data in the fragment, just call the handoff directly if + this is the first fragment or the PDU isn't fragmented. */ + if ( (!dcerpc_reassemble) || !(hdr->flags1 & PFCL1_FRAG) || + !tvb_bytes_exist(tvb, offset, stub_length) ) { + if (hdr->frag_num == 0) { + + + /* First fragment, possibly the only fragment */ + + /* + * XXX - authentication info? + */ + pinfo->fragmented = (hdr->flags1 & PFCL1_FRAG); + next_tvb = tvb_new_subset_length_caplen(tvb, offset, length, + reported_length); + dcerpc_try_handoff(pinfo, tree, dcerpc_tree, next_tvb, TRUE, hdr->drep, di, NULL); + } else { + /* PDU is fragmented and this isn't the first fragment */ + if (length > 0) { + proto_tree_add_item(dcerpc_tree, hf_dcerpc_fragment_data, tvb, offset, stub_length, ENC_NA); + } + } + } else { + /* Reassembly is enabled, the PDU is fragmented, and + we have all the data in the fragment; the first two + of those mean we should attempt reassembly, and the + third means we can attempt reassembly. */ + if (length > 0) { + proto_tree_add_item(dcerpc_tree, hf_dcerpc_fragment_data, tvb, offset, stub_length, ENC_NA); + } + + fd_head = fragment_add_seq(&dcerpc_cl_reassembly_table, + tvb, offset, + pinfo, hdr->seqnum, (void *)hdr, + hdr->frag_num, stub_length, + !(hdr->flags1 & PFCL1_LASTFRAG), 0); + if (fd_head != NULL) { + /* We completed reassembly... */ + if (pinfo->num == fd_head->reassembled_in) { + /* ...and this is the reassembled RPC PDU */ + next_tvb = tvb_new_chain(tvb, fd_head->tvb_data); + add_new_data_source(pinfo, next_tvb, "Reassembled DCE/RPC"); + show_fragment_seq_tree(fd_head, &dcerpc_frag_items, + tree, pinfo, next_tvb, &pi); + + /* + * XXX - authentication info? + */ + pinfo->fragmented = FALSE; + dcerpc_try_handoff(pinfo, tree, dcerpc_tree, next_tvb, TRUE, hdr->drep, di, NULL); + } else { + /* ...and this isn't the reassembled RPC PDU */ + pi = proto_tree_add_uint(dcerpc_tree, hf_dcerpc_reassembled_in, + tvb, 0, 0, fd_head->reassembled_in); + proto_item_set_generated(pi); + parent_pi = proto_tree_get_parent(dcerpc_tree); + if (parent_pi != NULL) { + proto_item_append_text(parent_pi, ", [Reas: #%u]", fd_head->reassembled_in); + } + col_append_fstr(pinfo->cinfo, COL_INFO, + " [DCE/RPC fragment, reas: #%u]", fd_head->reassembled_in); + } + } + } + pinfo->fragmented = save_fragmented; +} + +static void +dissect_dcerpc_dg_rqst(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *dcerpc_tree, proto_tree *tree, + e_dce_dg_common_hdr_t *hdr, conversation_t *conv) +{ + dcerpc_info *di; + dcerpc_call_value *value; + dcerpc_matched_key matched_key, *new_matched_key; + proto_item *pi; + proto_item *parent_pi; + + if (!(pinfo->fd->visited)) { + dcerpc_call_value *call_value; + dcerpc_dg_call_key *call_key; + + call_key = wmem_new(wmem_file_scope(), dcerpc_dg_call_key); + call_key->conv = conv; + call_key->seqnum = hdr->seqnum; + call_key->act_id = hdr->act_id; + + call_value = wmem_new(wmem_file_scope(), dcerpc_call_value); + call_value->uuid = hdr->if_id; + call_value->ver = hdr->if_ver; + call_value->object_uuid = hdr->obj_id; + call_value->opnum = hdr->opnum; + call_value->req_frame = pinfo->num; + call_value->req_time = pinfo->abs_ts; + call_value->rep_frame = 0; + call_value->max_ptr = 0; + call_value->se_data = NULL; + call_value->private_data = NULL; + call_value->pol = NULL; + /* NDR64 is not available on dg transports ?*/ + call_value->flags = 0; + + wmem_map_insert(dcerpc_dg_calls, call_key, call_value); + + new_matched_key = wmem_new(wmem_file_scope(), dcerpc_matched_key); + new_matched_key->frame = pinfo->num; + new_matched_key->call_id = hdr->seqnum; + wmem_map_insert(dcerpc_matched, new_matched_key, call_value); + } + + matched_key.frame = pinfo->num; + matched_key.call_id = hdr->seqnum; + value = (dcerpc_call_value *)wmem_map_lookup(dcerpc_matched, &matched_key); + if (!value) { + value = wmem_new(pinfo->pool, dcerpc_call_value); + value->uuid = hdr->if_id; + value->ver = hdr->if_ver; + value->object_uuid = hdr->obj_id; + value->opnum = hdr->opnum; + value->req_frame = pinfo->num; + value->rep_frame = 0; + value->max_ptr = 0; + value->se_data = NULL; + value->private_data = NULL; + } + + di = wmem_new0(pinfo->pool, dcerpc_info); + di->dcerpc_procedure_name = ""; + di->conv = conv; + di->call_id = hdr->seqnum; + di->transport_salt = -1; + di->ptype = PDU_REQ; + di->call_data = value; + + if (value->rep_frame != 0) { + pi = proto_tree_add_uint(dcerpc_tree, hf_dcerpc_response_in, + tvb, 0, 0, value->rep_frame); + proto_item_set_generated(pi); + parent_pi = proto_tree_get_parent(dcerpc_tree); + if (parent_pi != NULL) { + proto_item_append_text(parent_pi, ", [Resp: #%u]", value->rep_frame); + } + } + dissect_dcerpc_dg_stub(tvb, offset, pinfo, dcerpc_tree, tree, hdr, di); +} + +static void +dissect_dcerpc_dg_resp(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *dcerpc_tree, proto_tree *tree, + e_dce_dg_common_hdr_t *hdr, conversation_t *conv) +{ + dcerpc_info *di; + dcerpc_call_value *value; + dcerpc_matched_key matched_key, *new_matched_key; + proto_item *pi; + proto_item *parent_pi; + + if (!(pinfo->fd->visited)) { + dcerpc_call_value *call_value; + dcerpc_dg_call_key call_key; + + call_key.conv = conv; + call_key.seqnum = hdr->seqnum; + call_key.act_id = hdr->act_id; + + if ((call_value = (dcerpc_call_value *)wmem_map_lookup(dcerpc_dg_calls, &call_key))) { + new_matched_key = wmem_new(wmem_file_scope(), dcerpc_matched_key); + new_matched_key->frame = pinfo->num; + new_matched_key->call_id = hdr->seqnum; + wmem_map_insert(dcerpc_matched, new_matched_key, call_value); + if (call_value->rep_frame == 0) { + call_value->rep_frame = pinfo->num; + } + } + } + + matched_key.frame = pinfo->num; + matched_key.call_id = hdr->seqnum; + value = (dcerpc_call_value *)wmem_map_lookup(dcerpc_matched, &matched_key); + if (!value) { + value = wmem_new0(pinfo->pool, dcerpc_call_value); + value->uuid = hdr->if_id; + value->ver = hdr->if_ver; + value->object_uuid = hdr->obj_id; + value->opnum = hdr->opnum; + value->rep_frame = pinfo->num; + } + + di = wmem_new0(pinfo->pool, dcerpc_info); + di->dcerpc_procedure_name = ""; + di->conv = conv; + di->transport_salt = -1; + di->ptype = PDU_RESP; + di->call_data = value; + + if (value->req_frame != 0) { + nstime_t delta_ts; + pi = proto_tree_add_uint(dcerpc_tree, hf_dcerpc_request_in, + tvb, 0, 0, value->req_frame); + proto_item_set_generated(pi); + parent_pi = proto_tree_get_parent(dcerpc_tree); + if (parent_pi != NULL) { + proto_item_append_text(parent_pi, ", [Req: #%u]", value->req_frame); + } + nstime_delta(&delta_ts, &pinfo->abs_ts, &value->req_time); + pi = proto_tree_add_time(dcerpc_tree, hf_dcerpc_time, tvb, offset, 0, &delta_ts); + proto_item_set_generated(pi); + } else { + proto_tree_add_expert(dcerpc_tree, pinfo, &ei_dcerpc_no_request_found, tvb, 0, 0); + } + dissect_dcerpc_dg_stub(tvb, offset, pinfo, dcerpc_tree, tree, hdr, di); +} + +static void +dissect_dcerpc_dg_ping_ack(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *dcerpc_tree, + e_dce_dg_common_hdr_t *hdr, conversation_t *conv) +{ + proto_item *parent_pi; +/* if (!(pinfo->fd->visited)) {*/ + dcerpc_call_value *call_value; + dcerpc_dg_call_key call_key; + + call_key.conv = conv; + call_key.seqnum = hdr->seqnum; + call_key.act_id = hdr->act_id; + + if ((call_value = (dcerpc_call_value *)wmem_map_lookup(dcerpc_dg_calls, &call_key))) { + proto_item *pi; + nstime_t delta_ts; + + pi = proto_tree_add_uint(dcerpc_tree, hf_dcerpc_request_in, + tvb, 0, 0, call_value->req_frame); + proto_item_set_generated(pi); + parent_pi = proto_tree_get_parent(dcerpc_tree); + if (parent_pi != NULL) { + proto_item_append_text(parent_pi, ", [Req: #%u]", call_value->req_frame); + } + + col_append_fstr(pinfo->cinfo, COL_INFO, " [req: #%u]", call_value->req_frame); + + nstime_delta(&delta_ts, &pinfo->abs_ts, &call_value->req_time); + pi = proto_tree_add_time(dcerpc_tree, hf_dcerpc_time, tvb, offset, 0, &delta_ts); + proto_item_set_generated(pi); +/* }*/ + } +} + +/* + * DCERPC dissector for connectionless calls + */ +static gboolean +dissect_dcerpc_dg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + proto_item *ti = NULL; + proto_tree *dcerpc_tree = NULL; + e_dce_dg_common_hdr_t hdr; + int offset = 0; + conversation_t *conv; + int auth_level; + char *uuid_str; + const char *uuid_name = NULL; + static int * const hdr_flags1[] = { + &hf_dcerpc_dg_flags1_rsrvd_80, + &hf_dcerpc_dg_flags1_broadcast, + &hf_dcerpc_dg_flags1_idempotent, + &hf_dcerpc_dg_flags1_maybe, + &hf_dcerpc_dg_flags1_nofack, + &hf_dcerpc_dg_flags1_frag, + &hf_dcerpc_dg_flags1_last_frag, + &hf_dcerpc_dg_flags1_rsrvd_01, + NULL + }; + + static int * const hdr_flags2[] = { + &hf_dcerpc_dg_flags2_rsrvd_80, + &hf_dcerpc_dg_flags2_rsrvd_40, + &hf_dcerpc_dg_flags2_rsrvd_20, + &hf_dcerpc_dg_flags2_rsrvd_10, + &hf_dcerpc_dg_flags2_rsrvd_08, + &hf_dcerpc_dg_flags2_rsrvd_04, + &hf_dcerpc_dg_flags2_cancel_pending, + &hf_dcerpc_dg_flags2_rsrvd_01, + NULL + }; + + /* + * Check if this looks like a CL DCERPC call. All dg packets + * have an 80 byte header on them. Which starts with + * version (4), pkt_type. + */ + if (tvb_reported_length(tvb) < sizeof (hdr)) { + return FALSE; + } + + /* Version must be 4 */ + hdr.rpc_ver = tvb_get_guint8(tvb, offset++); + if (hdr.rpc_ver != 4) + return FALSE; + + /* Type must be <= PDU_CANCEL_ACK or it's not connectionless DCE/RPC */ + hdr.ptype = tvb_get_guint8(tvb, offset++); + if (hdr.ptype > PDU_CANCEL_ACK) + return FALSE; + + /* flags1 has bit 1 and 8 as reserved for implementations, with no + indication that they must be set to 0, so we don't check them. + */ + hdr.flags1 = tvb_get_guint8(tvb, offset++); + + /* flags2 has bit 1 reserved for implementations, bit 2 used, + and the other bits reserved for future use and specified + as "must be set to 0", so if any of the other bits are set + it is probably not DCE/RPC. + */ + hdr.flags2 = tvb_get_guint8(tvb, offset++); + if (hdr.flags2&0xfc) + return FALSE; + + tvb_memcpy(tvb, (guint8 *)hdr.drep, offset, sizeof (hdr.drep)); + offset += (int)sizeof (hdr.drep); + if (hdr.drep[0]&0xee) + return FALSE; + if (hdr.drep[1] > DCE_RPC_DREP_FP_IBM) + return FALSE; + + col_set_str(pinfo->cinfo, COL_PROTOCOL, "DCERPC"); + col_add_str(pinfo->cinfo, COL_INFO, pckt_vals[hdr.ptype].strptr); + + hdr.serial_hi = tvb_get_guint8(tvb, offset++); + dcerpc_tvb_get_uuid(tvb, offset, hdr.drep, &hdr.obj_id); + offset += 16; + dcerpc_tvb_get_uuid(tvb, offset, hdr.drep, &hdr.if_id); + offset += 16; + dcerpc_tvb_get_uuid(tvb, offset, hdr.drep, &hdr.act_id); + offset += 16; + hdr.server_boot = dcerpc_tvb_get_ntohl(tvb, offset, hdr.drep); + offset += 4; + hdr.if_ver = dcerpc_tvb_get_ntohl(tvb, offset, hdr.drep); + offset += 4; + hdr.seqnum = dcerpc_tvb_get_ntohl(tvb, offset, hdr.drep); + offset += 4; + hdr.opnum = dcerpc_tvb_get_ntohs(tvb, offset, hdr.drep); + offset += 2; + hdr.ihint = dcerpc_tvb_get_ntohs(tvb, offset, hdr.drep); + offset += 2; + hdr.ahint = dcerpc_tvb_get_ntohs(tvb, offset, hdr.drep); + offset += 2; + hdr.frag_len = dcerpc_tvb_get_ntohs(tvb, offset, hdr.drep); + offset += 2; + hdr.frag_num = dcerpc_tvb_get_ntohs(tvb, offset, hdr.drep); + offset += 2; + hdr.auth_proto = tvb_get_guint8(tvb, offset++); + hdr.serial_lo = tvb_get_guint8(tvb, offset++); + + if (tree) { + ti = proto_tree_add_item(tree, proto_dcerpc, tvb, 0, -1, ENC_NA); + if (ti) { + dcerpc_tree = proto_item_add_subtree(ti, ett_dcerpc); + proto_item_append_text(ti, " %s, Seq: %u, Serial: %u, Frag: %u, FragLen: %u", + val_to_str(hdr.ptype, pckt_vals, "Unknown (0x%02x)"), + hdr.seqnum, hdr.serial_hi*256+hdr.serial_lo, + hdr.frag_num, hdr.frag_len); + } + } + offset = 0; + + proto_tree_add_uint(dcerpc_tree, hf_dcerpc_ver, tvb, offset, 1, hdr.rpc_ver); + offset++; + + proto_tree_add_uint(dcerpc_tree, hf_dcerpc_packet_type, tvb, offset, 1, hdr.ptype); + offset++; + + proto_tree_add_bitmask_value(dcerpc_tree, tvb, offset, hf_dcerpc_dg_flags1, + ett_dcerpc_dg_flags1, hdr_flags1, hdr.flags1); + offset++; + + proto_tree_add_bitmask_value(dcerpc_tree, tvb, offset, hf_dcerpc_dg_flags2, + ett_dcerpc_dg_flags2, hdr_flags2, hdr.flags2); + offset++; + + if (tree) { + proto_tree_add_dcerpc_drep(dcerpc_tree, tvb, offset, hdr.drep, (int)sizeof (hdr.drep)); + } + offset += (int)sizeof (hdr.drep); + + if (tree) + proto_tree_add_uint(dcerpc_tree, hf_dcerpc_dg_serial_hi, tvb, offset, 1, hdr.serial_hi); + offset++; + + if (tree) { + proto_tree_add_guid_format(dcerpc_tree, hf_dcerpc_obj_id, tvb, + offset, 16, (e_guid_t *) &hdr.obj_id, "Object UUID: %s", + guid_to_str(pinfo->pool, (e_guid_t *) &hdr.obj_id)); + } + offset += 16; + + if (tree) { + uuid_str = guid_to_str(pinfo->pool, (e_guid_t*)&hdr.if_id); + uuid_name = guids_get_uuid_name(&hdr.if_id, pinfo->pool); + if (uuid_name) { + proto_tree_add_guid_format(dcerpc_tree, hf_dcerpc_dg_if_id, tvb, + offset, 16, (e_guid_t *) &hdr.if_id, "Interface: %s UUID: %s", uuid_name, uuid_str); + } else { + proto_tree_add_guid_format(dcerpc_tree, hf_dcerpc_dg_if_id, tvb, + offset, 16, (e_guid_t *) &hdr.if_id, "Interface UUID: %s", uuid_str); + } + } + offset += 16; + + if (tree) { + proto_tree_add_guid_format(dcerpc_tree, hf_dcerpc_dg_act_id, tvb, + offset, 16, (e_guid_t *) &hdr.act_id, "Activity: %s", + guid_to_str(pinfo->pool, (e_guid_t *) &hdr.act_id)); + } + offset += 16; + + if (tree) { + nstime_t server_boot; + + server_boot.secs = hdr.server_boot; + server_boot.nsecs = 0; + + if (hdr.server_boot == 0) + proto_tree_add_time_format_value(dcerpc_tree, hf_dcerpc_dg_server_boot, + tvb, offset, 4, &server_boot, + "Unknown (0)"); + else + proto_tree_add_time(dcerpc_tree, hf_dcerpc_dg_server_boot, + tvb, offset, 4, &server_boot); + } + offset += 4; + + if (tree) + proto_tree_add_uint(dcerpc_tree, hf_dcerpc_dg_if_ver, tvb, offset, 4, hdr.if_ver); + offset += 4; + + if (tree) + proto_tree_add_uint(dcerpc_tree, hf_dcerpc_dg_seqnum, tvb, offset, 4, hdr.seqnum); + col_append_fstr(pinfo->cinfo, COL_INFO, ": seq: %u", hdr.seqnum); + offset += 4; + + if (tree) + proto_tree_add_uint(dcerpc_tree, hf_dcerpc_opnum, tvb, offset, 2, hdr.opnum); + offset += 2; + + if (tree) + proto_tree_add_uint(dcerpc_tree, hf_dcerpc_dg_ihint, tvb, offset, 2, hdr.ihint); + offset += 2; + + if (tree) + proto_tree_add_uint(dcerpc_tree, hf_dcerpc_dg_ahint, tvb, offset, 2, hdr.ahint); + offset += 2; + + if (tree) + proto_tree_add_uint(dcerpc_tree, hf_dcerpc_dg_frag_len, tvb, offset, 2, hdr.frag_len); + offset += 2; + + if (tree) + proto_tree_add_uint(dcerpc_tree, hf_dcerpc_dg_frag_num, tvb, offset, 2, hdr.frag_num); + if (hdr.flags1 & PFCL1_FRAG) { + /* Fragmented - put the fragment number into the Info column */ + col_append_fstr(pinfo->cinfo, COL_INFO, " frag: %u", + hdr.frag_num); + } + offset += 2; + + if (tree) + proto_tree_add_uint(dcerpc_tree, hf_dcerpc_dg_auth_proto, tvb, offset, 1, hdr.auth_proto); + offset++; + + if (tree) + proto_tree_add_uint(dcerpc_tree, hf_dcerpc_dg_serial_lo, tvb, offset, 1, hdr.serial_lo); + if (hdr.flags1 & PFCL1_FRAG) { + /* Fragmented - put the serial number into the Info column */ + col_append_fstr(pinfo->cinfo, COL_INFO, " serial: %u", + (hdr.serial_hi << 8) | hdr.serial_lo); + } + offset++; + + if (tree) { + /* + * XXX - for Kerberos, we get a protection level; if it's + * DCE_C_AUTHN_LEVEL_PKT_PRIVACY, we can't dissect the + * stub data. + */ + dissect_dcerpc_dg_auth(tvb, offset, dcerpc_tree, &hdr, + &auth_level); + } + + /* + * keeping track of the conversation shouldn't really be necessary + * for connectionless packets, because everything we need to know + * to dissect is in the header for each packet. Unfortunately, + * Microsoft's implementation is buggy and often puts the + * completely wrong if_id in the header. go figure. So, keep + * track of the seqnum and use that if possible. Note: that's not + * completely correct. It should really be done based on both the + * activity_id and seqnum. I haven't seen anywhere that it would + * make a difference, but for future reference... + */ + conv = find_or_create_conversation(pinfo); + + /* + * Packet type specific stuff is next. + */ + + switch (hdr.ptype) { + + case PDU_CANCEL_ACK: + /* Body is optional */ + /* XXX - we assume "frag_len" is the length of the body */ + if (hdr.frag_len != 0) + dissect_dcerpc_dg_cancel_ack(tvb, offset, pinfo, dcerpc_tree, &hdr); + break; + + case PDU_CL_CANCEL: + /* + * XXX - The DCE RPC 1.1 spec doesn't say the body is optional, + * but in at least one capture none of the Cl_cancel PDUs had a + * body. + */ + /* XXX - we assume "frag_len" is the length of the body */ + if (hdr.frag_len != 0) + dissect_dcerpc_dg_cancel(tvb, offset, pinfo, dcerpc_tree, &hdr); + break; + + case PDU_NOCALL: + /* Body is optional; if present, it's the same as PDU_FACK */ + /* XXX - we assume "frag_len" is the length of the body */ + if (hdr.frag_len != 0) + dissect_dcerpc_dg_fack(tvb, offset, pinfo, dcerpc_tree, &hdr); + break; + + case PDU_FACK: + /* Body is optional */ + /* XXX - we assume "frag_len" is the length of the body */ + if (hdr.frag_len != 0) + dissect_dcerpc_dg_fack(tvb, offset, pinfo, dcerpc_tree, &hdr); + break; + + case PDU_REJECT: + case PDU_FAULT: + dissect_dcerpc_dg_reject_fault(tvb, offset, pinfo, dcerpc_tree, &hdr); + break; + + case PDU_REQ: + dissect_dcerpc_dg_rqst(tvb, offset, pinfo, dcerpc_tree, tree, &hdr, conv); + break; + + case PDU_RESP: + dissect_dcerpc_dg_resp(tvb, offset, pinfo, dcerpc_tree, tree, &hdr, conv); + break; + + /* these requests have no body */ + case PDU_ACK: + case PDU_PING: + dissect_dcerpc_dg_ping_ack(tvb, offset, pinfo, dcerpc_tree, &hdr, conv); + break; + case PDU_WORKING: + default: + break; + } + + return TRUE; +} + +static void +dcerpc_auth_subdissector_list_free(gpointer p, gpointer user_data _U_) +{ + g_free(p); +} + +static void +dcerpc_shutdown(void) +{ + g_slist_foreach(dcerpc_auth_subdissector_list, dcerpc_auth_subdissector_list_free, NULL); + g_slist_free(dcerpc_auth_subdissector_list); + g_hash_table_destroy(dcerpc_uuids); + tvb_free(tvb_trailer_signature); +} + +void +proto_register_dcerpc(void) +{ + static hf_register_info hf[] = { + { &hf_dcerpc_request_in, + { "Request in frame", "dcerpc.request_in", FT_FRAMENUM, BASE_NONE, + NULL, 0, "This packet is a response to the packet with this number", HFILL }}, + { &hf_dcerpc_response_in, + { "Response in frame", "dcerpc.response_in", FT_FRAMENUM, BASE_NONE, + NULL, 0, "This packet will be responded in the packet with this number", HFILL }}, + { &hf_dcerpc_referent_id32, + { "Referent ID", "dcerpc.referent_id", FT_UINT32, BASE_HEX, + NULL, 0, "Referent ID for this NDR encoded pointer", HFILL }}, + { &hf_dcerpc_referent_id64, + { "Referent ID", "dcerpc.referent_id64", FT_UINT64, BASE_HEX, + NULL, 0, "Referent ID for this NDR encoded pointer", HFILL }}, + { &hf_dcerpc_ver, + { "Version", "dcerpc.ver", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_ver_minor, + { "Version (minor)", "dcerpc.ver_minor", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_packet_type, + { "Packet type", "dcerpc.pkt_type", FT_UINT8, BASE_DEC, VALS(pckt_vals), 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_flags, + { "Packet Flags", "dcerpc.cn_flags", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_flags_first_frag, + { "First Frag", "dcerpc.cn_flags.first_frag", FT_BOOLEAN, 8, TFS(&tfs_set_notset), PFC_FIRST_FRAG, NULL, HFILL }}, + { &hf_dcerpc_cn_flags_last_frag, + { "Last Frag", "dcerpc.cn_flags.last_frag", FT_BOOLEAN, 8, TFS(&tfs_set_notset), PFC_LAST_FRAG, NULL, HFILL }}, + { &hf_dcerpc_cn_flags_cancel_pending, + { "Cancel Pending", "dcerpc.cn_flags.cancel_pending", FT_BOOLEAN, 8, TFS(&tfs_set_notset), PFC_PENDING_CANCEL, NULL, HFILL }}, + { &hf_dcerpc_cn_flags_reserved, + { "Reserved", "dcerpc.cn_flags.reserved", FT_BOOLEAN, 8, TFS(&tfs_set_notset), PFC_RESERVED_1, NULL, HFILL }}, + { &hf_dcerpc_cn_flags_mpx, + { "Multiplex", "dcerpc.cn_flags.mpx", FT_BOOLEAN, 8, TFS(&tfs_set_notset), PFC_CONC_MPX, NULL, HFILL }}, + { &hf_dcerpc_cn_flags_dne, + { "Did Not Execute", "dcerpc.cn_flags.dne", FT_BOOLEAN, 8, TFS(&tfs_set_notset), PFC_DID_NOT_EXECUTE, NULL, HFILL }}, + { &hf_dcerpc_cn_flags_maybe, + { "Maybe", "dcerpc.cn_flags.maybe", FT_BOOLEAN, 8, TFS(&tfs_set_notset), PFC_MAYBE, NULL, HFILL }}, + { &hf_dcerpc_cn_flags_object, + { "Object", "dcerpc.cn_flags.object", FT_BOOLEAN, 8, TFS(&tfs_set_notset), PFC_OBJECT_UUID, NULL, HFILL }}, + { &hf_dcerpc_drep, + { "Data Representation", "dcerpc.drep", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_drep_byteorder, + { "Byte order", "dcerpc.drep.byteorder", FT_UINT8, BASE_DEC, VALS(drep_byteorder_vals), 0x0, NULL, HFILL }}, + { &hf_dcerpc_ndr_padding, + { "NDR-Padding", "dcerpc.ndr_padding", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_drep_character, + { "Character", "dcerpc.drep.character", FT_UINT8, BASE_DEC, VALS(drep_character_vals), 0x0, NULL, HFILL }}, + { &hf_dcerpc_drep_fp, + { "Floating-point", "dcerpc.drep.fp", FT_UINT8, BASE_DEC, VALS(drep_fp_vals), 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_frag_len, + { "Frag Length", "dcerpc.cn_frag_len", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_auth_len, + { "Auth Length", "dcerpc.cn_auth_len", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_call_id, + { "Call ID", "dcerpc.cn_call_id", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_max_xmit, + { "Max Xmit Frag", "dcerpc.cn_max_xmit", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_max_recv, + { "Max Recv Frag", "dcerpc.cn_max_recv", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_assoc_group, + { "Assoc Group", "dcerpc.cn_assoc_group", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_num_ctx_items, + { "Num Ctx Items", "dcerpc.cn_num_ctx_items", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_ctx_item, + { "Ctx Item", "dcerpc.cn_ctx_item", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_ctx_id, + { "Context ID", "dcerpc.cn_ctx_id", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_num_trans_items, + { "Num Trans Items", "dcerpc.cn_num_trans_items", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_bind_abstract_syntax, + { "Abstract Syntax", "dcerpc.cn_bind_abstract_syntax", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_bind_if_id, + { "Interface UUID", "dcerpc.cn_bind_to_uuid", FT_GUID, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_bind_if_ver, + { "Interface Ver", "dcerpc.cn_bind_if_ver", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_bind_if_ver_minor, + { "Interface Ver Minor", "dcerpc.cn_bind_if_ver_minor", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_bind_trans_syntax, + { "Transfer Syntax", "dcerpc.cn_bind_trans", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_bind_trans_id, + { "ID", "dcerpc.cn_bind_trans_id", FT_GUID, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_bind_trans_ver, + { "ver", "dcerpc.cn_bind_trans_ver", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_bind_trans_btfn, /* [MS-RPCE] 2.2.2.14 */ + {"Bind Time Features", "dcerpc.cn_bind_trans_btfn", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }}, + { &hf_dcerpc_cn_bind_trans_btfn_01, + { "Security Context Multiplexing Supported", "dcerpc.cn_bind_trans_btfn.01", FT_BOOLEAN, 16, NULL, 0x0001, NULL, HFILL }}, + { &hf_dcerpc_cn_bind_trans_btfn_02, + { "Keep Connection On Orphan Supported", "dcerpc.cn_bind_trans_btfn.02", FT_BOOLEAN, 16, NULL, 0x0002, NULL, HFILL }}, + { &hf_dcerpc_cn_alloc_hint, + { "Alloc hint", "dcerpc.cn_alloc_hint", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_sec_addr_len, + { "Scndry Addr len", "dcerpc.cn_sec_addr_len", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_sec_addr, + { "Scndry Addr", "dcerpc.cn_sec_addr", FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_num_results, + { "Num results", "dcerpc.cn_num_results", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_ack_result, + { "Ack result", "dcerpc.cn_ack_result", FT_UINT16, BASE_DEC, VALS(p_cont_result_vals), 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_ack_reason, + { "Ack reason", "dcerpc.cn_ack_reason", FT_UINT16, BASE_DEC, VALS(p_provider_reason_vals), 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_ack_trans_id, + { "Transfer Syntax", "dcerpc.cn_ack_trans_id", FT_GUID, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_ack_trans_ver, + { "Syntax ver", "dcerpc.cn_ack_trans_ver", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_reject_reason, + { "Reject reason", "dcerpc.cn_reject_reason", FT_UINT16, BASE_DEC, VALS(reject_reason_vals), 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_num_protocols, + { "Number of protocols", "dcerpc.cn_num_protocols", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_protocol_ver_major, + { "Protocol major version", "dcerpc.cn_protocol_ver_major", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_protocol_ver_minor, + { "Protocol minor version", "dcerpc.cn_protocol_ver_minor", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_cancel_count, + { "Cancel count", "dcerpc.cn_cancel_count", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_fault_flags, + { "Fault flags", "dcerpc.cn_fault_flags", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_fault_flags_extended_error_info, + { "Extended error information present", "dcerpc.cn_fault_flags.extended_error", FT_BOOLEAN, 8, NULL, 0x1, NULL, HFILL }}, + { &hf_dcerpc_cn_status, + { "Status", "dcerpc.cn_status", FT_UINT32, BASE_HEX, VALS(reject_status_vals), 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_deseg_req, + { "Desegmentation Required", "dcerpc.cn_deseg_req", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_auth_type, + { "Auth type", "dcerpc.auth_type", FT_UINT8, BASE_DEC, VALS(authn_protocol_vals), 0x0, NULL, HFILL }}, + { &hf_dcerpc_auth_level, + { "Auth level", "dcerpc.auth_level", FT_UINT8, BASE_DEC, VALS(authn_level_vals), 0x0, NULL, HFILL }}, + { &hf_dcerpc_auth_pad_len, + { "Auth pad len", "dcerpc.auth_pad_len", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_auth_rsrvd, + { "Auth Rsrvd", "dcerpc.auth_rsrvd", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_auth_ctx_id, + { "Auth Context ID", "dcerpc.auth_ctx_id", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_dg_flags1, + { "Flags1", "dcerpc.dg_flags1", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_dg_flags1_rsrvd_01, + { "Reserved for implementation", "dcerpc.dg_flags1_rsrvd_01", FT_BOOLEAN, 8, TFS(&tfs_set_notset), PFCL1_RESERVED_01, NULL, HFILL }}, + { &hf_dcerpc_dg_flags1_last_frag, + { "Last Fragment", "dcerpc.dg_flags1_last_frag", FT_BOOLEAN, 8, TFS(&tfs_set_notset), PFCL1_LASTFRAG, NULL, HFILL }}, + { &hf_dcerpc_dg_flags1_frag, + { "Fragment", "dcerpc.dg_flags1_frag", FT_BOOLEAN, 8, TFS(&tfs_set_notset), PFCL1_FRAG, NULL, HFILL }}, + { &hf_dcerpc_dg_flags1_nofack, + { "No Fack", "dcerpc.dg_flags1_nofack", FT_BOOLEAN, 8, TFS(&tfs_set_notset), PFCL1_NOFACK, NULL, HFILL }}, + { &hf_dcerpc_dg_flags1_maybe, + { "Maybe", "dcerpc.dg_flags1_maybe", FT_BOOLEAN, 8, TFS(&tfs_set_notset), PFCL1_MAYBE, NULL, HFILL }}, + { &hf_dcerpc_dg_flags1_idempotent, + { "Idempotent", "dcerpc.dg_flags1_idempotent", FT_BOOLEAN, 8, TFS(&tfs_set_notset), PFCL1_IDEMPOTENT, NULL, HFILL }}, + { &hf_dcerpc_dg_flags1_broadcast, + { "Broadcast", "dcerpc.dg_flags1_broadcast", FT_BOOLEAN, 8, TFS(&tfs_set_notset), PFCL1_BROADCAST, NULL, HFILL }}, + { &hf_dcerpc_dg_flags1_rsrvd_80, + { "Reserved for implementation", "dcerpc.dg_flags1_rsrvd_80", FT_BOOLEAN, 8, TFS(&tfs_set_notset), PFCL1_RESERVED_80, NULL, HFILL }}, + { &hf_dcerpc_dg_flags2, + { "Flags2", "dcerpc.dg_flags2", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_dg_flags2_rsrvd_01, + { "Reserved for implementation", "dcerpc.dg_flags2_rsrvd_01", FT_BOOLEAN, 8, TFS(&tfs_set_notset), PFCL2_RESERVED_01, NULL, HFILL }}, + { &hf_dcerpc_dg_flags2_cancel_pending, + { "Cancel Pending", "dcerpc.dg_flags2_cancel_pending", FT_BOOLEAN, 8, TFS(&tfs_set_notset), PFCL2_CANCEL_PENDING, NULL, HFILL }}, + { &hf_dcerpc_dg_flags2_rsrvd_04, + { "Reserved for future use (MBZ)", "dcerpc.dg_flags2_rsrvd_04", FT_BOOLEAN, 8, TFS(&tfs_set_notset), PFCL2_RESERVED_04, NULL, HFILL }}, + { &hf_dcerpc_dg_flags2_rsrvd_08, + { "Reserved for future use (MBZ)", "dcerpc.dg_flags2_rsrvd_08", FT_BOOLEAN, 8, TFS(&tfs_set_notset), PFCL2_RESERVED_08, NULL, HFILL }}, + { &hf_dcerpc_dg_flags2_rsrvd_10, + { "Reserved for future use (MBZ)", "dcerpc.dg_flags2_rsrvd_10", FT_BOOLEAN, 8, TFS(&tfs_set_notset), PFCL2_RESERVED_10, NULL, HFILL }}, + { &hf_dcerpc_dg_flags2_rsrvd_20, + { "Reserved for future use (MBZ)", "dcerpc.dg_flags2_rsrvd_20", FT_BOOLEAN, 8, TFS(&tfs_set_notset), PFCL2_RESERVED_20, NULL, HFILL }}, + { &hf_dcerpc_dg_flags2_rsrvd_40, + { "Reserved for future use (MBZ)", "dcerpc.dg_flags2_rsrvd_40", FT_BOOLEAN, 8, TFS(&tfs_set_notset), PFCL2_RESERVED_40, NULL, HFILL }}, + { &hf_dcerpc_dg_flags2_rsrvd_80, + { "Reserved for future use (MBZ)", "dcerpc.dg_flags2_rsrvd_80", FT_BOOLEAN, 8, TFS(&tfs_set_notset), PFCL2_RESERVED_80, NULL, HFILL }}, + { &hf_dcerpc_dg_serial_lo, + { "Serial Low", "dcerpc.dg_serial_lo", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_dg_serial_hi, + { "Serial High", "dcerpc.dg_serial_hi", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_dg_ahint, + { "Activity Hint", "dcerpc.dg_ahint", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_dg_ihint, + { "Interface Hint", "dcerpc.dg_ihint", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_dg_frag_len, + { "Fragment len", "dcerpc.dg_frag_len", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_dg_frag_num, + { "Fragment num", "dcerpc.dg_frag_num", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_dg_auth_proto, + { "Auth proto", "dcerpc.dg_auth_proto", FT_UINT8, BASE_DEC, VALS(authn_protocol_vals), 0x0, NULL, HFILL }}, + { &hf_dcerpc_dg_seqnum, + { "Sequence num", "dcerpc.dg_seqnum", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_dg_server_boot, + { "Server boot time", "dcerpc.dg_server_boot", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_dg_if_ver, + { "Interface Ver", "dcerpc.dg_if_ver", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_krb5_av_prot_level, + { "Protection Level", "dcerpc.krb5_av.prot_level", FT_UINT8, BASE_DEC, VALS(authn_level_vals), 0x0, NULL, HFILL }}, + { &hf_dcerpc_krb5_av_key_vers_num, + { "Key Version Number", "dcerpc.krb5_av.key_vers_num", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_krb5_av_key_auth_verifier, + { "Authentication Verifier", "dcerpc.krb5_av.auth_verifier", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_obj_id, + { "Object", "dcerpc.obj_id", FT_GUID, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_dg_if_id, + { "Interface", "dcerpc.dg_if_id", FT_GUID, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_dg_act_id, + { "Activity", "dcerpc.dg_act_id", FT_GUID, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_opnum, + { "Opnum", "dcerpc.opnum", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + + { &hf_dcerpc_dg_cancel_vers, + { "Cancel Version", "dcerpc.dg_cancel_vers", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + + { &hf_dcerpc_dg_cancel_id, + { "Cancel ID", "dcerpc.dg_cancel_id", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + + { &hf_dcerpc_dg_server_accepting_cancels, + { "Server accepting cancels", "dcerpc.server_accepting_cancels", FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + + { &hf_dcerpc_dg_fack_vers, + { "FACK Version", "dcerpc.fack_vers", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + + { &hf_dcerpc_dg_fack_window_size, + { "Window Size", "dcerpc.fack_window_size", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + + { &hf_dcerpc_dg_fack_max_tsdu, + { "Max TSDU", "dcerpc.fack_max_tsdu", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + + { &hf_dcerpc_dg_fack_max_frag_size, + { "Max Frag Size", "dcerpc.fack_max_frag_size", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + + { &hf_dcerpc_dg_fack_serial_num, + { "Serial Num", "dcerpc.fack_serial_num", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + + { &hf_dcerpc_dg_fack_selack_len, + { "Selective ACK Len", "dcerpc.fack_selack_len", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + + { &hf_dcerpc_dg_fack_selack, + { "Selective ACK", "dcerpc.fack_selack", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + + { &hf_dcerpc_dg_status, + { "Status", "dcerpc.dg_status", FT_UINT32, BASE_HEX, VALS(reject_status_vals), 0x0, NULL, HFILL }}, + + { &hf_dcerpc_array_max_count, + { "Max Count", "dcerpc.array.max_count", FT_UINT32, BASE_DEC, NULL, 0x0, "Maximum Count: Number of elements in the array", HFILL }}, + + { &hf_dcerpc_array_offset, + { "Offset", "dcerpc.array.offset", FT_UINT32, BASE_DEC, NULL, 0x0, "Offset for first element in array", HFILL }}, + + { &hf_dcerpc_array_actual_count, + { "Actual Count", "dcerpc.array.actual_count", FT_UINT32, BASE_DEC, NULL, 0x0, "Actual Count: Actual number of elements in the array", HFILL }}, + + { &hf_dcerpc_op, + { "Operation", "dcerpc.op", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + + { &hf_dcerpc_null_pointer, + { "NULL Pointer", "dcerpc.null_pointer", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + + { &hf_dcerpc_fragments, + { "Reassembled DCE/RPC Fragments", "dcerpc.fragments", FT_NONE, BASE_NONE, + NULL, 0x0, "DCE/RPC Fragments", HFILL }}, + + { &hf_dcerpc_fragment, + { "DCE/RPC Fragment", "dcerpc.fragment", FT_FRAMENUM, BASE_NONE, + NULL, 0x0, NULL, HFILL }}, + + { &hf_dcerpc_fragment_overlap, + { "Fragment overlap", "dcerpc.fragment.overlap", FT_BOOLEAN, BASE_NONE, + NULL, 0x0, "Fragment overlaps with other fragments", HFILL }}, + + { &hf_dcerpc_fragment_overlap_conflict, + { "Conflicting data in fragment overlap", "dcerpc.fragment.overlap.conflict", FT_BOOLEAN, BASE_NONE, + NULL, 0x0, "Overlapping fragments contained conflicting data", HFILL }}, + + { &hf_dcerpc_fragment_multiple_tails, + { "Multiple tail fragments found", "dcerpc.fragment.multipletails", FT_BOOLEAN, BASE_NONE, + NULL, 0x0, "Several tails were found when defragmenting the packet", HFILL }}, + + { &hf_dcerpc_fragment_too_long_fragment, + { "Fragment too long", "dcerpc.fragment.toolongfragment", FT_BOOLEAN, BASE_NONE, + NULL, 0x0, "Fragment contained data past end of packet", HFILL }}, + + { &hf_dcerpc_fragment_error, + { "Defragmentation error", "dcerpc.fragment.error", FT_FRAMENUM, BASE_NONE, + NULL, 0x0, "Defragmentation error due to illegal fragments", HFILL }}, + + { &hf_dcerpc_fragment_count, + { "Fragment count", "dcerpc.fragment.count", FT_UINT32, BASE_DEC, + NULL, 0x0, NULL, HFILL }}, + + { &hf_dcerpc_time, + { "Time from request", "dcerpc.time", FT_RELATIVE_TIME, BASE_NONE, + NULL, 0, "Time between Request and Response for DCE-RPC calls", HFILL }}, + + { &hf_dcerpc_reassembled_in, + { "Reassembled PDU in frame", "dcerpc.reassembled_in", FT_FRAMENUM, BASE_NONE, + NULL, 0x0, "The DCE/RPC PDU is completely reassembled in the packet with this number", HFILL }}, + + { &hf_dcerpc_reassembled_length, + { "Reassembled DCE/RPC length", "dcerpc.reassembled.length", FT_UINT32, BASE_DEC, + NULL, 0x0, "The total length of the reassembled payload", HFILL }}, + + { &hf_dcerpc_unknown_if_id, + { "Unknown DCERPC interface id", "dcerpc.unknown_if_id", FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + + { &hf_dcerpc_cn_rts_flags, + { "RTS Flags", "dcerpc.cn_rts_flags", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_rts_flags_ping, + { "Ping", "dcerpc.cn_rts.flags.ping", FT_BOOLEAN, 8, TFS(&tfs_set_notset), RTS_FLAG_PING, NULL, HFILL }}, + { &hf_dcerpc_cn_rts_flags_other_cmd, + { "Other Cmd", "dcerpc.cn_rts_flags.other_cmd", FT_BOOLEAN, 8, TFS(&tfs_set_notset), RTS_FLAG_OTHER_CMD, NULL, HFILL }}, + { &hf_dcerpc_cn_rts_flags_recycle_channel, + { "Recycle Channel", "dcerpc.cn_rts_flags.recycle_channel", FT_BOOLEAN, 8, TFS(&tfs_set_notset), RTS_FLAG_RECYCLE_CHANNEL, NULL, HFILL }}, + { &hf_dcerpc_cn_rts_flags_in_channel, + { "In Channel", "dcerpc.cn_rts_flags.in_channel", FT_BOOLEAN, 8, TFS(&tfs_set_notset), RTS_FLAG_IN_CHANNEL, NULL, HFILL }}, + { &hf_dcerpc_cn_rts_flags_out_channel, + { "Out Channel", "dcerpc.cn_rts_flags.out_channel", FT_BOOLEAN, 8, TFS(&tfs_set_notset), RTS_FLAG_OUT_CHANNEL, NULL, HFILL }}, + { &hf_dcerpc_cn_rts_flags_eof, + { "EOF", "dcerpc.cn_rts_flags.eof", FT_BOOLEAN, 8, TFS(&tfs_set_notset), RTS_FLAG_EOF, NULL, HFILL }}, + { &hf_dcerpc_cn_rts_commands_nb, + { "RTS Number of Commands", "dcerpc.cn_rts_commands_nb", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_rts_command, + { "RTS Command", "dcerpc.cn_rts_command", FT_UINT32, BASE_HEX, VALS(rts_command_vals), 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_rts_command_receivewindowsize, + {"Receive Window Size", "dcerpc.cn_rts_command.receivewindowsize", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_rts_command_fack_bytesreceived, + {"Bytes Received", "dcerpc.cn_rts_command.fack.bytesreceived", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_rts_command_fack_availablewindow, + {"Available Window", "dcerpc.cn_rts_command.fack.availablewindow", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_rts_command_fack_channelcookie, + {"Channel Cookie", "dcerpc.cn_rts_command.fack.channelcookie", FT_GUID, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_rts_command_connectiontimeout, + {"Connection Timeout", "dcerpc.cn_rts_command.connectiontimeout", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_rts_command_cookie, + {"Cookie", "dcerpc.cn_rts_command.cookie", FT_GUID, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_rts_command_channellifetime, + {"Channel Lifetime", "dcerpc.cn_rts_command.channellifetime", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_rts_command_clientkeepalive, + {"Client Keepalive", "dcerpc.cn_rts_command.clientkeepalive", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_rts_command_version, + {"Version", "dcerpc.cn_rts_command.version", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_rts_command_conformancecount, + {"Conformance Count", "dcerpc.cn_rts_command.padding.conformancecount", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_rts_command_padding, + { "Padding", "dcerpc.cn_rts_command.padding.padding", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}}, + { &hf_dcerpc_cn_rts_command_addrtype, + { "Address Type", "dcerpc.cn_rts_command.addrtype", FT_UINT32, BASE_DEC, VALS(rts_addresstype_vals), 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_rts_command_associationgroupid, + {"Association Group ID", "dcerpc.cn_rts_command.associationgroupid", FT_GUID, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_rts_command_forwarddestination, + {"Forward Destination", "dcerpc.cn_rts_command.forwarddestination", FT_UINT32, BASE_DEC, VALS(rts_forward_destination_vals), 0x0, NULL, HFILL }}, + { &hf_dcerpc_cn_rts_command_pingtrafficsentnotify, + {"Ping Traffic Sent Notify", "dcerpc.cn_rts_command.pingtrafficsentnotify", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_sec_vt_signature, + {"SEC_VT_SIGNATURE", "dcerpc.rpc_sec_vt.signature", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_sec_vt_command_end, + {"SEC_VT_COMMAND_END", "dcerpc.rpc_sec_vt.command.end", FT_BOOLEAN, 16, NULL, 0x4000, NULL, HFILL }}, + { &hf_dcerpc_sec_vt_command_must, + {"SEC_VT_MUST_PROCESS_COMMAND", "dcerpc.rpc_sec_vt.command.must_process", FT_BOOLEAN, 16, NULL, 0x8000, NULL, HFILL }}, + { &hf_dcerpc_sec_vt_command_cmd, + {"Cmd", "dcerpc.rpc_sec_vt.command.cmd", FT_UINT16, BASE_HEX, VALS(sec_vt_command_cmd_vals), 0x3fff, NULL, HFILL }}, + { &hf_dcerpc_sec_vt_command, + {"Command", "dcerpc.rpc_sec_vt.command", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }}, + { &hf_dcerpc_sec_vt_command_length, + {"Length", "dcerpc.rpc_sec_vt.command.length", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL}}, + { &hf_dcerpc_sec_vt_bitmask, + {"rpc_sec_vt_bitmask", "dcerpc.rpc_sec_vt.bitmask", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }}, + { &hf_dcerpc_sec_vt_bitmask_sign, + {"CLIENT_SUPPORT_HEADER_SIGNING", "dcerpc.rpc_sec_vt.bitmask.sign", FT_BOOLEAN, 32, NULL, 0x1, NULL, HFILL }}, + { &hf_dcerpc_sec_vt_pcontext_uuid, + {"UUID", "dcerpc.rpc_sec_vt.pcontext.interface.uuid", FT_GUID, BASE_NONE, NULL, 0, NULL, HFILL }}, + { &hf_dcerpc_sec_vt_pcontext_ver, + {"Version", "dcerpc.rpc_sec_vt.pcontext.interface.ver", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }}, + { &hf_dcerpc_reserved, + {"Reserved", "dcerpc.reserved", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }}, + { &hf_dcerpc_unknown, + {"Unknown", "dcerpc.unknown", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }}, + { &hf_dcerpc_missalign, + {"missalign", "dcerpc.missalign", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }}, + /* Generated from convert_proto_tree_add_text.pl */ + { &hf_dcerpc_duplicate_ptr, { "duplicate PTR", "dcerpc.duplicate_ptr", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_encrypted_stub_data, { "Encrypted stub data", "dcerpc.encrypted_stub_data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_decrypted_stub_data, { "Decrypted stub data", "dcerpc.decrypted_stub_data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_payload_stub_data, { "Payload stub data", "dcerpc.payload_stub_data", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_stub_data_with_sec_vt, { "Stub data with rpc_sec_verification_trailer", "dcerpc.stub_data_with_sec_vt", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_stub_data, { "Stub data", "dcerpc.stub_data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_auth_padding, { "Auth Padding", "dcerpc.auth_padding", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_auth_info, { "Auth Info", "dcerpc.auth_info", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_auth_credentials, { "Auth Credentials", "dcerpc.auth_credentials", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_fault_stub_data, { "Fault stub data", "dcerpc.fault_stub_data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_fragment_data, { "Fragment data", "dcerpc.fragment_data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cmd_client_ipv4, { "RTS Client address", "dcerpc.cmd_client_ipv4", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_cmd_client_ipv6, { "RTS Client address", "dcerpc.cmd_client_ipv6", FT_IPv6, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_dcerpc_authentication_verifier, { "Authentication verifier", "dcerpc.authentication_verifier", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + }; + static gint *ett[] = { + &ett_dcerpc, + &ett_dcerpc_cn_flags, + &ett_dcerpc_cn_ctx, + &ett_dcerpc_cn_iface, + &ett_dcerpc_cn_trans_syntax, + &ett_dcerpc_cn_trans_btfn, + &ett_dcerpc_cn_bind_trans_btfn, + &ett_dcerpc_cn_rts_flags, + &ett_dcerpc_cn_rts_command, + &ett_dcerpc_cn_rts_pdu, + &ett_dcerpc_drep, + &ett_dcerpc_dg_flags1, + &ett_dcerpc_dg_flags2, + &ett_dcerpc_pointer_data, + &ett_dcerpc_string, + &ett_dcerpc_fragments, + &ett_dcerpc_fragment, + &ett_dcerpc_krb5_auth_verf, + &ett_dcerpc_auth_info, + &ett_dcerpc_verification_trailer, + &ett_dcerpc_sec_vt_command, + &ett_dcerpc_sec_vt_bitmask, + &ett_dcerpc_sec_vt_pcontext, + &ett_dcerpc_sec_vt_header, + &ett_dcerpc_complete_stub_data, + &ett_dcerpc_fault_flags, + &ett_dcerpc_fault_stub_data, + }; + + static ei_register_info ei[] = { + { &ei_dcerpc_fragment, { "dcerpc.fragment.reassemble", PI_REASSEMBLE, PI_CHAT, "Fragment", EXPFILL }}, + { &ei_dcerpc_fragment_reassembled, { "dcerpc.fragment_reassembled", PI_REASSEMBLE, PI_CHAT, "Fragment, reassembled", EXPFILL }}, + { &ei_dcerpc_cn_ctx_id_no_bind, { "dcerpc.cn_ctx_id.no_bind", PI_UNDECODED, PI_NOTE, "No bind info for interface Context ID", EXPFILL }}, + { &ei_dcerpc_no_request_found, { "dcerpc.no_request_found", PI_SEQUENCE, PI_NOTE, "No request to this DCE/RPC call found", EXPFILL }}, + { &ei_dcerpc_cn_status, { "dcerpc.cn_status.expert", PI_RESPONSE_CODE, PI_NOTE, "Fault", EXPFILL }}, + { &ei_dcerpc_fragment_multiple, { "dcerpc.fragment_multiple", PI_SEQUENCE, PI_CHAT, "Multiple DCE/RPC fragments/PDU's in one packet", EXPFILL }}, +#if 0 /* XXX - too much "output noise", removed for now */ + { &ei_dcerpc_context_change, { "dcerpc.context_change", PI_SEQUENCE, PI_CHAT, "Context change", EXPFILL }}, +#endif + { &ei_dcerpc_bind_not_acknowledged, { "dcerpc.bind_not_acknowledged", PI_SEQUENCE, PI_WARN, "Bind not acknowledged", EXPFILL }}, + { &ei_dcerpc_verifier_unavailable, { "dcerpc.verifier_unavailable", PI_UNDECODED, PI_WARN, "Verifier unavailable", EXPFILL }}, + { &ei_dcerpc_invalid_pdu_authentication_attempt, { "dcerpc.invalid_pdu_authentication_attempt", PI_UNDECODED, PI_WARN, "Invalid authentication attempt", EXPFILL }}, + /* Generated from convert_proto_tree_add_text.pl */ + { &ei_dcerpc_long_frame, { "dcerpc.long_frame", PI_PROTOCOL, PI_WARN, "Long frame", EXPFILL }}, + { &ei_dcerpc_cn_rts_command, { "dcerpc.cn_rts_command.unknown", PI_PROTOCOL, PI_WARN, "unknown RTS command number", EXPFILL }}, + { &ei_dcerpc_not_implemented, { "dcerpc.not_implemented", PI_UNDECODED, PI_WARN, "dissection not implemented", EXPFILL }}, + }; + + /* Decode As handling */ + static build_valid_func dcerpc_da_build_value[1] = {dcerpc_value}; + static decode_as_value_t dcerpc_da_values = {dcerpc_prompt, 1, dcerpc_da_build_value}; + static decode_as_t dcerpc_da = {"dcerpc", "dcerpc.uuid", + 1, 0, &dcerpc_da_values, NULL, NULL, + dcerpc_populate_list, decode_dcerpc_binding_reset, dcerpc_decode_as_change, dcerpc_decode_as_free}; + + module_t *dcerpc_module; + expert_module_t* expert_dcerpc; + + proto_dcerpc = proto_register_protocol("Distributed Computing Environment / Remote Procedure Call (DCE/RPC)", "DCERPC", "dcerpc"); + proto_register_field_array(proto_dcerpc, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + expert_dcerpc = expert_register_protocol(proto_dcerpc); + expert_register_field_array(expert_dcerpc, ei, array_length(ei)); + + uuid_dissector_table = register_dissector_table(DCERPC_TABLE_NAME, "DCE/RPC UUIDs", proto_dcerpc, FT_GUID, BASE_HEX); + + /* structures and data for BIND */ + dcerpc_binds = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), dcerpc_bind_hash, dcerpc_bind_equal); + + dcerpc_auths = wmem_map_new_autoreset(wmem_epan_scope(), + wmem_file_scope(), + dcerpc_auth_context_hash, + dcerpc_auth_context_equal); + + /* structures and data for CALL */ + dcerpc_cn_calls = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), dcerpc_cn_call_hash, dcerpc_cn_call_equal); + dcerpc_dg_calls = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), dcerpc_dg_call_hash, dcerpc_dg_call_equal); + + /* structure and data for MATCHED */ + dcerpc_matched = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), dcerpc_matched_hash, dcerpc_matched_equal); + + register_init_routine(decode_dcerpc_inject_bindings); + + dcerpc_module = prefs_register_protocol(proto_dcerpc, NULL); + prefs_register_bool_preference(dcerpc_module, + "desegment_dcerpc", + "Reassemble DCE/RPC messages spanning multiple TCP segments", + "Whether the DCE/RPC dissector should reassemble messages" + " spanning multiple TCP segments." + " To use this option, you must also enable" + " \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.", + &dcerpc_cn_desegment); + prefs_register_bool_preference(dcerpc_module, + "reassemble_dcerpc", + "Reassemble DCE/RPC fragments", + "Whether the DCE/RPC dissector should reassemble fragmented DCE/RPC PDUs", + &dcerpc_reassemble); + + /* + * XXX - addresses_ports_reassembly_table_functions? + * Or can a single connection-oriented DCE RPC session persist + * over multiple transport layer connections? + */ + reassembly_table_register(&dcerpc_co_reassembly_table, + &addresses_reassembly_table_functions); + reassembly_table_register(&dcerpc_cl_reassembly_table, + &dcerpc_cl_reassembly_table_functions); + + dcerpc_uuids = g_hash_table_new_full(dcerpc_uuid_hash, dcerpc_uuid_equal, g_free, g_free); + dcerpc_tap = register_tap("dcerpc"); + + register_decode_as(&dcerpc_da); + + register_srt_table(proto_dcerpc, NULL, 1, dcerpcstat_packet, dcerpcstat_init, dcerpcstat_param); + + tvb_trailer_signature = tvb_new_real_data(TRAILER_SIGNATURE, + sizeof(TRAILER_SIGNATURE), + sizeof(TRAILER_SIGNATURE)); + + dcerpc_tcp_handle = register_dissector("dcerpc.tcp", dissect_dcerpc_tcp, proto_dcerpc); + + register_shutdown_routine(dcerpc_shutdown); +} + +void +proto_reg_handoff_dcerpc(void) +{ + heur_dissector_add("tcp", dissect_dcerpc_tcp_heur, "DCE/RPC over TCP", "dcerpc_tcp", proto_dcerpc, HEURISTIC_ENABLE); + heur_dissector_add("netbios", dissect_dcerpc_cn_pk, "DCE/RPC over NetBios", "dcerpc_netbios", proto_dcerpc, HEURISTIC_ENABLE); + heur_dissector_add("udp", dissect_dcerpc_dg, "DCE/RPC over UDP", "dcerpc_udp", proto_dcerpc, HEURISTIC_ENABLE); + heur_dissector_add("smb_transact", dissect_dcerpc_cn_smbpipe, "DCE/RPC over SMB", "dcerpc_smb_transact", proto_dcerpc, HEURISTIC_ENABLE); + heur_dissector_add("smb2_pipe_subdissectors", dissect_dcerpc_cn_smb2, "DCE/RPC over SMB2", "dcerpc_smb2", proto_dcerpc, HEURISTIC_ENABLE); + heur_dissector_add("http", dissect_dcerpc_cn_bs, "DCE/RPC over HTTP", "dcerpc_http", proto_dcerpc, HEURISTIC_ENABLE); + dcerpc_smb_init(proto_dcerpc); + + dissector_add_for_decode_as("tcp.port", dcerpc_tcp_handle); + + guids_add_uuid(&uuid_data_repr_proto, "32bit NDR"); + guids_add_uuid(&uuid_ndr64, "64bit NDR"); + guids_add_uuid(&uuid_asyncemsmdb, "async MAPI"); +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ |