D-Bus: Add conversation tracking

This commit is contained in:
Simon Holesch 2021-02-02 00:57:32 +01:00 committed by AndersBroman
parent e207d65830
commit 76301761bb
1 changed files with 147 additions and 3 deletions

View File

@ -19,6 +19,7 @@
#include <epan/expert.h>
#include <epan/ptvcursor.h>
#include <wsutil/ws_roundup.h>
#include <epan/conversation.h>
#include "packet-tcp.h"
#define DBUS_MAX_ARRAY_LEN (64 * 1024 * 1024)
@ -101,6 +102,11 @@ static const value_string endianness_vals[] = {
{ 'B', "big-endian" },
{ 0, NULL }
};
#define DBUS_FLAGS_NO_REPLY_EXPECTED_MASK 0x01
#define DBUS_FLAGS_NO_AUTO_START_MASK 0x02
#define DBUS_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION_MASK 0x04
static const true_false_string allow_vals = { "Allow", "Don't allow" };
static const true_false_string no_start_vals = { "Don't start", "Start" };
static const true_false_string not_expected_vals = { "Not expected", "Expected" };
@ -146,6 +152,9 @@ static int hf_dbus_type_variant_signature = -1;
static int hf_dbus_type_dict_entry = -1;
static int hf_dbus_type_dict_entry_key = -1;
static int hf_dbus_type_unix_fd = -1;
static int hf_dbus_response_in = -1;
static int hf_dbus_response_to = -1;
static int hf_dbus_response_time = -1;
static int ett_dbus = -1;
static int ett_dbus_flags = -1;
@ -184,6 +193,7 @@ typedef struct {
packet_info *pinfo;
guint enc;
guint32 message_type;
guint8 flags;
guint32 body_len;
guint32 serial;
@ -226,6 +236,18 @@ typedef union {
const char *string;
} dbus_val_t;
typedef struct {
guint32 req_frame;
guint32 rep_frame;
nstime_t req_time;
} dbus_transaction_t;
typedef struct {
wmem_map_t *packets;
} dbus_conv_info_t;
static wmem_map_t *request_info_map;
static gboolean
is_ascii_digit(char c) {
return (guint)c - '0' < 10;
@ -920,6 +942,111 @@ dissect_dbus_body(dbus_packet_t *packet) {
return err;
}
static void
add_conversation(dbus_packet_t *packet, proto_tree *header_field_tree) {
gboolean is_request;
gchar *request_dest;
gchar *key;
if (!packet->sender || !packet->destination) {
// in a peer-to-peer setup, sender and destination can be unset, in which case conversation tracking
// doesn't make sense.
return;
}
switch (packet->message_type) {
case DBUS_MESSAGE_TYPE_METHOD_CALL:
if (packet->flags & DBUS_FLAGS_NO_REPLY_EXPECTED_MASK) {
// there won't be a response, no need to track
return;
}
is_request = TRUE;
// There are cases where the destination address of the request doesn't match the sender address of the
// response, for example:
// - The request is sent to the well-known bus name (e.g. org.freedesktop.PolicyKit1), but the reply is
// sent from the unique connection name (e.g. :1.10). Both names refer to the same connection and for
// every unique connection name, there can be multiple well-known names.
// - The D-Bus daemon (org.freedesktop.DBus) sends the reply, instead of the recipient, e.g.
// org.freedesktop.DBus.Error.AccessDenied
// To find the correct response, match the request sender + serial with the response destination +
// reply serial.
if (!PINFO_FD_VISITED(packet->pinfo)) {
request_dest = wmem_strdup(wmem_file_scope(), packet->destination);
key = wmem_strdup_printf(wmem_file_scope(), "%s %u", packet->sender, packet->serial);
wmem_map_insert(request_info_map, key, request_dest);
}
break;
case DBUS_MESSAGE_TYPE_METHOD_RETURN:
case DBUS_MESSAGE_TYPE_ERROR:
is_request = FALSE;
key = wmem_strdup_printf(wmem_packet_scope(), "%s %u", packet->destination, packet->reply_serial);
request_dest = (gchar *)wmem_map_lookup(request_info_map, key);
if (request_dest && !g_str_equal(request_dest, packet->sender)) {
// Replace the sender address of the response with the destination address of the request, so
// the conversation can be found.
address sender_addr;
set_address(&sender_addr, AT_STRINGZ, (int)strlen(request_dest)+1, request_dest);
conversation_create_endpoint(packet->pinfo, &sender_addr, &packet->pinfo->dst,
conversation_pt_to_endpoint_type(packet->pinfo->ptype),
packet->pinfo->srcport, packet->pinfo->destport);
}
break;
case DBUS_MESSAGE_TYPE_SIGNAL:
default:
// signals don't have a response, no need to track
return;
}
conversation_t *conversation = find_or_create_conversation(packet->pinfo);
dbus_conv_info_t *conv_info = (dbus_conv_info_t *)conversation_get_proto_data(conversation, proto_dbus);
if (!conv_info) {
conv_info = wmem_new(wmem_file_scope(), dbus_conv_info_t);
conv_info->packets = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
conversation_add_proto_data(conversation, proto_dbus, conv_info);
}
dbus_transaction_t *trans;
if (!PINFO_FD_VISITED(packet->pinfo)) {
if (is_request) {
trans = wmem_new(wmem_file_scope(), dbus_transaction_t);
*trans = (dbus_transaction_t){
.req_frame = packet->pinfo->num,
.req_time = packet->pinfo->fd->abs_ts,
};
wmem_map_insert(conv_info->packets, GUINT_TO_POINTER(packet->serial), (void *)trans);
} else {
trans = (dbus_transaction_t *)wmem_map_lookup(conv_info->packets, GUINT_TO_POINTER(packet->reply_serial));
if (trans) {
trans->rep_frame = packet->pinfo->num;
}
}
} else {
guint32 request_serial = is_request ? packet->serial : packet->reply_serial;
trans = (dbus_transaction_t *)wmem_map_lookup(conv_info->packets, GUINT_TO_POINTER(request_serial));
}
if (!trans) {
return;
}
if (is_request) {
proto_item *it = proto_tree_add_uint(header_field_tree, hf_dbus_response_in, ptvcursor_tvbuff(packet->cursor), 0, 0, trans->rep_frame);
proto_item_set_generated(it);
} else {
nstime_t ns;
proto_item *it = proto_tree_add_uint(header_field_tree, hf_dbus_response_to, ptvcursor_tvbuff(packet->cursor), 0, 0, trans->req_frame);
proto_item_set_generated(it);
nstime_delta(&ns, &packet->pinfo->fd->abs_ts, &trans->req_time);
it = proto_tree_add_time(header_field_tree, hf_dbus_response_time, ptvcursor_tvbuff(packet->cursor), 0, 0, &ns);
proto_item_set_generated(it);
}
}
static int
dissect_dbus_header_fields(dbus_packet_t *packet) {
dbus_type_reader_t root_reader = {
@ -1107,6 +1234,8 @@ dissect_dbus_header_fields(dbus_packet_t *packet) {
break;
}
add_conversation(packet, proto_item_get_subtree(header_field_array_pi));
// Header length must be a multiple of 8 bytes
return add_padding(packet, SIG_CODE_STRUCT_OPEN);
}
@ -1148,6 +1277,7 @@ dissect_dbus_header(dbus_packet_t *packet) {
ptvcursor_add_no_advance(packet->cursor, hf_dbus_flags_no_reply_expected, 1, packet->enc);
ptvcursor_add_no_advance(packet->cursor, hf_dbus_flags_no_auto_start, 1, packet->enc);
ptvcursor_add_no_advance(packet->cursor, hf_dbus_flags_allow_interactive_authorization, 1, packet->enc);
packet->flags = tvb_get_guint8(ptvcursor_tvbuff(packet->cursor), ptvcursor_current_offset(packet->cursor));
ptvcursor_advance(packet->cursor, 1);
ptvcursor_pop_subtree(packet->cursor);
@ -1241,11 +1371,14 @@ proto_register_dbus(void) {
{ &hf_dbus_flags, { "Message Flags", "dbus.flags",
FT_UINT8, BASE_HEX, NULL, 0x00, NULL, HFILL }},
{ &hf_dbus_flags_no_reply_expected, { "No Reply Expected", "dbus.flags.no_reply_expected",
FT_BOOLEAN, 8, TFS(&not_expected_vals), 0x01, NULL, HFILL }},
FT_BOOLEAN, 8, TFS(&not_expected_vals),
DBUS_FLAGS_NO_REPLY_EXPECTED_MASK, NULL, HFILL }},
{ &hf_dbus_flags_no_auto_start, { "No Auto Start", "dbus.flags.no_auto_start",
FT_BOOLEAN, 8, TFS(&no_start_vals), 0x02, NULL, HFILL }},
FT_BOOLEAN, 8, TFS(&no_start_vals),
DBUS_FLAGS_NO_AUTO_START_MASK, NULL, HFILL }},
{ &hf_dbus_flags_allow_interactive_authorization, { "Allow Interactive Authorization", "dbus.flags.allow_interactive_authorization",
FT_BOOLEAN, 8, TFS(&allow_vals), 0x04, NULL, HFILL }},
FT_BOOLEAN, 8, TFS(&allow_vals),
DBUS_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION_MASK, NULL, HFILL }},
{ &hf_dbus_version, { "Protocol Version", "dbus.version",
FT_UINT8, BASE_DEC, NULL, 0x00, NULL, HFILL }},
{ &hf_dbus_body_length, { "Message Body Length", "dbus.body_length",
@ -1316,6 +1449,15 @@ proto_register_dbus(void) {
FT_BYTES, BASE_NO_DISPLAY_VALUE, NULL, 0x00, NULL, HFILL }},
{ &hf_dbus_type_unix_fd, { "Unix FD", "dbus.type.unix_fd",
FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL }},
{ &hf_dbus_response_in, { "Response In", "dbus.response_in",
FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0x0,
"The response to this D-Bus call is in this frame", HFILL }},
{ &hf_dbus_response_to, { "Response To", "dbus.response_to",
FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0x0,
"This is a response to the D-Bus call in this frame", HFILL }},
{ &hf_dbus_response_time, { "Response Time", "dbus.response_time",
FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
"The time between the Call and the Reply", HFILL }},
};
static gint *ett[] = {
@ -1385,6 +1527,8 @@ proto_register_dbus(void) {
dbus_handle = create_dissector_handle(dissect_dbus, proto_dbus);
dbus_handle_tcp = create_dissector_handle(dissect_dbus_tcp, proto_dbus);
request_info_map = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), wmem_str_hash, g_str_equal);
}
void