diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:34:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:34:10 +0000 |
commit | e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch) | |
tree | 68cb5ef9081156392f1dd62a00c6ccc1451b93df /plugins/epan/falco_bridge | |
parent | Initial commit. (diff) | |
download | wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.tar.xz wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.zip |
Adding upstream version 4.2.2.upstream/4.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plugins/epan/falco_bridge')
-rw-r--r-- | plugins/epan/falco_bridge/AUTHORS | 2 | ||||
-rw-r--r-- | plugins/epan/falco_bridge/CMakeLists.txt | 82 | ||||
-rw-r--r-- | plugins/epan/falco_bridge/README.md | 56 | ||||
-rw-r--r-- | plugins/epan/falco_bridge/packet-falco-bridge.c | 676 | ||||
-rw-r--r-- | plugins/epan/falco_bridge/sinsp-span.cpp | 239 | ||||
-rw-r--r-- | plugins/epan/falco_bridge/sinsp-span.h | 80 |
6 files changed, 1135 insertions, 0 deletions
diff --git a/plugins/epan/falco_bridge/AUTHORS b/plugins/epan/falco_bridge/AUTHORS new file mode 100644 index 00000000..2265263f --- /dev/null +++ b/plugins/epan/falco_bridge/AUTHORS @@ -0,0 +1,2 @@ +Author : +Loris Degioanni
\ No newline at end of file diff --git a/plugins/epan/falco_bridge/CMakeLists.txt b/plugins/epan/falco_bridge/CMakeLists.txt new file mode 100644 index 00000000..384d81d2 --- /dev/null +++ b/plugins/epan/falco_bridge/CMakeLists.txt @@ -0,0 +1,82 @@ +# CMakeLists.txt +# +# Wireshark - Network traffic analyzer +# By Gerald Combs <gerald@wireshark.org> +# Copyright 1998 Gerald Combs +# +# SPDX-License-Identifier: GPL-2.0-or-later +# + +include(WiresharkPlugin) + +# Plugin name and version info (major minor micro extra) +set_module_info(falco-bridge 0 0 4 0) + +set(DISSECTOR_SRC + packet-falco-bridge.c + sinsp-span.cpp +) + +set(DISSECTOR_HEADERS + conversation-macros.h + sinsp-span.h +) + +set(PLUGIN_FILES + plugin.c + ${DISSECTOR_SRC} +) + +set_source_files_properties( + ${PLUGIN_FILES} + PROPERTIES + COMPILE_FLAGS "${WERROR_COMMON_FLAGS}" +) + +register_plugin_files(plugin.c + plugin + ${DISSECTOR_SRC} +) + +add_logray_plugin_library(falco-bridge epan) + +# XXX Hacks; need to fix in falcosecurity-libs. +target_compile_definitions(falco-bridge PRIVATE + HAVE_STRLCPY=1 + ) +# target_compile_options(falco-bridge PRIVATE -Wno-address-of-packed-member) + +target_include_directories(falco-bridge SYSTEM PRIVATE + ${SINSP_INCLUDE_DIRS} +) + +target_link_libraries(falco-bridge + epan + ${SINSP_LINK_LIBRARIES} +) + +install_plugin(falco-bridge epan) + +CHECKAPI( + NAME + falco-bridge + SWITCHES + --group dissectors-prohibited + --group dissectors-restricted + SOURCES + ${DISSECTOR_SRC} + ${DISSECTOR_HEADERS} +) + +# +# Editor modelines - https://www.wireshark.org/tools/modelines.html +# +# Local variables: +# c-basic-offset: 8 +# tab-width: 8 +# indent-tabs-mode: t +# End: +# +# vi: set shiftwidth=8 tabstop=8 noexpandtab: +# :indentSize=8:tabSize=8:noTabs=false: +# diff --git a/plugins/epan/falco_bridge/README.md b/plugins/epan/falco_bridge/README.md new file mode 100644 index 00000000..d88276c7 --- /dev/null +++ b/plugins/epan/falco_bridge/README.md @@ -0,0 +1,56 @@ +# Falco Bridge + +This plugin is a bridge between [Falco plugins](https://github.com/falcosecurity/plugins/) and Wireshark, so that Falco plugins can be used as dissectors. +It requires [libsinsp and libscap](https://github.com/falcosecurity/libs/). + +## Building the Falco Bridge plugin + +1. Download and compile [libsinsp and libscap](https://github.com/falcosecurity/libs/). + You will probably want to pass `-DMINIMAL_BUILD=ON -DCREATE_TEST_TARGETS=OFF` to cmake. + +1. Configure Wireshark with + +``` +cmake \ + -DSINSP_INCLUDEDIR=/path/to/falcosecurity-libs \ + -DSINSP_LIBDIR=/path/to/falcosecurity-libs/ \ + -DFALCO_PLUGINS="/path/to/plugin1;/path/to/plugin2;/path/to/plugin3" \ + [other cmake args] +``` + +## Quick Start + +1. Create a directory named "falco" at the same level as the "epan" plugin folder. +You can find the global and per-user plugin folder locations on your system in About → Folders or in the [User's Guide](https://www.wireshark.org/docs/wsug_html_chunked/ChPluginFolders.html). + +1. Build your desired [Falco plugin](https://github.com/falcosecurity/plugins/) and place it in the "falco" plugin directory. + +## Conversations + +Falco plugins can mark individual fields with a conversation flag (EPF_CONVERSATION). +The Falco Bridge dissector treats each of these as separate conversations, and for features such as navigation and packet list marking, the _first_ conversation field is used for matching packets. + +## Licensing + +libsinsp and libscap are released under the Apache 2.0 license. +They depend on the following libraries: + +- b64: MIT +- c-ares: MIT +- curl: MIT +- GRPC: Apache 2.0 +- jq: MIT +- JsonCpp: MIT +- LuaJIT: MIT +- OpenSSL < 3.0: SSLeay +- OpenSSL >= 3.0 : Apache 2.0 +- Protobuf: BSD-3-Clause +- oneTBB: Apache 2.0 +- zlib: zlib + +Wireshark is released under the GPL version 2 (GPL-2.0-or-later). It and the Apache-2.0 license are compatible via the "any later version" provision in the GPL version 2. +As discussed at https://www.wireshark.org/lists/wireshark-dev/202203/msg00020.html, combining Wireshark and libsinsp+libscap should be OK, but that in effect invokes the GPLv2's "any later version" provision, making the Wireshark portion of the combined work GPLv3+. + +Debian would appear to concur: https://lists.debian.org/debian-legal/2014/08/msg00102.html. + +No version of the GPL is compatible with the SSLeay license; you must ensure that libsinsp+libscap is linked with OpenSSL 3.0 or later. diff --git a/plugins/epan/falco_bridge/packet-falco-bridge.c b/plugins/epan/falco_bridge/packet-falco-bridge.c new file mode 100644 index 00000000..81dbc6c1 --- /dev/null +++ b/plugins/epan/falco_bridge/packet-falco-bridge.c @@ -0,0 +1,676 @@ +/* packet-falco-bridge.c + * + * By Loris Degioanni + * Copyright (C) 2021 Sysdig, Inc. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +// To do: +// - Convert this to C++? It would let us get rid of the glue that is +// sinsp-span and make string handling a lot easier. However, +// epan/address.h and driver/ppm_events_public.h both define PT_NONE. +// - Add a configuration preference for configure_plugin? +// - Add a configuration preference for individual conversation filters vs ANDing them? +// We would need to add deregister_(|log_)conversation_filter before we implement this. + +#include "config.h" + +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> + +#ifndef _WIN32 +#include <unistd.h> +#include <dlfcn.h> +#endif + +#include <epan/exceptions.h> +#include <epan/packet.h> +#include <epan/proto.h> +#include <epan/proto_data.h> +#include <epan/conversation.h> +#include <epan/conversation_filter.h> +#include <epan/tap.h> +#include <epan/stat_tap_ui.h> + +#include <wsutil/file_util.h> +#include <wsutil/filesystem.h> +#include <wsutil/inet_addr.h> +#include <wsutil/report_message.h> + +#include "sinsp-span.h" + +typedef enum bridge_field_flags_e { + BFF_NONE = 0, + BFF_HIDDEN = 1 << 1, // Unused + BFF_INFO = 1 << 2, + BFF_CONVERSATION = 1 << 3 +} bridge_field_flags_e; + +typedef struct conv_filter_info { + hf_register_info *field_info; + bool is_present; + wmem_strbuf_t *strbuf; +} conv_filter_info; + +typedef struct bridge_info { + sinsp_source_info_t *ssi; + uint32_t source_id; + int proto; + hf_register_info* hf; + int* hf_ids; + hf_register_info* hf_v4; + int *hf_v4_ids; + hf_register_info* hf_v6; + int *hf_v6_ids; + int* hf_id_to_addr_id; // Maps an hf offset to an hf_v[46] offset + uint32_t visible_fields; + uint32_t* field_flags; + int* field_ids; + uint32_t num_conversation_filters; + conv_filter_info *conversation_filters; +} bridge_info; + +static int proto_falco_bridge = -1; +static gint ett_falco_bridge = -1; +static gint ett_sinsp_span = -1; +static gint ett_address = -1; +static dissector_table_t ptype_dissector_table; + +static int dissect_falco_bridge(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data); +static int dissect_sinsp_span(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_); + +/* + * Array of plugin bridges + */ +bridge_info* bridges = NULL; +guint nbridges = 0; +guint n_conv_fields = 0; + +/* + * sinsp extractor span + */ +sinsp_span_t *sinsp_span = NULL; + +/* + * Fields + */ +static int hf_sdp_source_id_size = -1; +static int hf_sdp_lengths = -1; +static int hf_sdp_source_id = -1; + +static hf_register_info hf[] = { + { &hf_sdp_source_id_size, + { "Plugin ID size", "falcobridge.id.size", + FT_UINT32, BASE_DEC, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_sdp_lengths, + { "Field Lengths", "falcobridge.lens", + FT_UINT32, BASE_DEC, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_sdp_source_id, + { "Plugin ID", "falcobridge.id", + FT_UINT32, BASE_DEC, + NULL, 0x0, + NULL, HFILL } + }, +}; + +// Returns true if the field might contain an IPv4 or IPv6 address. +// XXX This should probably be a preference. +static bool is_addr_field(const char *abbrev) { + if (strstr(abbrev, ".srcip")) { // ct.srcip + return true; + } else if (strstr(abbrev, ".client.ip")) { // okta.client.ip + return true; + } + return false; +} + +static gboolean +is_filter_valid(packet_info *pinfo, void *cfi_ptr) +{ + conv_filter_info *cfi = (conv_filter_info *)cfi_ptr; + + if (!cfi->is_present) { + return FALSE; + } + + int proto_id = proto_registrar_get_parent(cfi->field_info->hfinfo.id); + + if (proto_id < 0) { + return false; + } + + return proto_is_frame_protocol(pinfo->layers, proto_registrar_get_nth(proto_id)->abbrev); +} + +static gchar* +build_filter(packet_info *pinfo _U_, void *cfi_ptr) +{ + conv_filter_info *cfi = (conv_filter_info *)cfi_ptr; + + if (!cfi->is_present) { + return FALSE; + } + + return ws_strdup_printf("%s eq %s", cfi->field_info->hfinfo.abbrev, cfi->strbuf->str); +} + +void +configure_plugin(bridge_info* bi, char* config _U_) +{ + /* + * Initialize the plugin + */ + bi->source_id = get_sinsp_source_id(bi->ssi); + + size_t tot_fields = get_sinsp_source_nfields(bi->ssi); + bi->visible_fields = 0; + uint32_t addr_fields = 0; + sinsp_field_info_t sfi; + bi->num_conversation_filters = 0; + + for (size_t j = 0; j < tot_fields; j++) { + get_sinsp_source_field_info(bi->ssi, j, &sfi); + if (sfi.is_hidden) { + /* + * Skip the fields that are marked as hidden. + * XXX Should we keep them and call proto_item_set_hidden? + */ + continue; + } + if (sfi.type == SFT_STRINGZ && is_addr_field(sfi.abbrev)) { + addr_fields++; + } + bi->visible_fields++; + + if (sfi.is_conversation) { + bi->num_conversation_filters++; + } + } + + if (bi->visible_fields) { + bi->hf = (hf_register_info*)wmem_alloc(wmem_epan_scope(), bi->visible_fields * sizeof(hf_register_info)); + bi->hf_ids = (int*)wmem_alloc(wmem_epan_scope(), bi->visible_fields * sizeof(int)); + bi->field_ids = (int*)wmem_alloc(wmem_epan_scope(), bi->visible_fields * sizeof(int)); + bi->field_flags = (guint32*)wmem_alloc(wmem_epan_scope(), bi->visible_fields * sizeof(guint32)); + + if (addr_fields) { + bi->hf_id_to_addr_id = (int *)wmem_alloc(wmem_epan_scope(), bi->visible_fields * sizeof(int)); + bi->hf_v4 = (hf_register_info*)wmem_alloc(wmem_epan_scope(), addr_fields * sizeof(hf_register_info)); + bi->hf_v4_ids = (int*)wmem_alloc(wmem_epan_scope(), addr_fields * sizeof(int)); + bi->hf_v6 = (hf_register_info*)wmem_alloc(wmem_epan_scope(), addr_fields * sizeof(hf_register_info)); + bi->hf_v6_ids = (int*)wmem_alloc(wmem_epan_scope(), addr_fields * sizeof(int)); + } + + if (bi->num_conversation_filters) { + bi->conversation_filters = (conv_filter_info *)wmem_alloc(wmem_epan_scope(), bi->num_conversation_filters * sizeof (conv_filter_info)); + } + + uint32_t fld_cnt = 0; + size_t conv_fld_cnt = 0; + uint32_t addr_fld_cnt = 0; + + for (size_t j = 0; j < tot_fields; j++) + { + bi->hf_ids[fld_cnt] = -1; + bi->field_ids[fld_cnt] = (int) j; + bi->field_flags[fld_cnt] = BFF_NONE; + hf_register_info* ri = bi->hf + fld_cnt; + + get_sinsp_source_field_info(bi->ssi, j, &sfi); + + if (sfi.is_hidden) { + /* + * Skip the fields that are marked as hidden + */ + continue; + } + + enum ftenum ftype; + int fdisplay = BASE_NONE; + switch (sfi.type) { + case SFT_STRINGZ: + ftype = FT_STRINGZ; + break; + case SFT_UINT64: + ftype = FT_UINT64; + switch (sfi.display_format) { + case SFDF_DECIMAL: + fdisplay = BASE_DEC; + break; + case SFDF_HEXADECIMAL: + fdisplay = BASE_HEX; + break; + case SFDF_OCTAL: + fdisplay = BASE_OCT; + break; + default: + THROW_FORMATTED(DissectorError, "error in plugin %s: display format %s is not supported", + get_sinsp_source_name(bi->ssi), + sfi.abbrev); + } + break; + default: + THROW_FORMATTED(DissectorError, "error in plugin %s: type of field %s is not supported", + get_sinsp_source_name(bi->ssi), + sfi.abbrev); + } + + hf_register_info finfo = { + bi->hf_ids + fld_cnt, + { + wmem_strdup(wmem_epan_scope(), sfi.display), wmem_strdup(wmem_epan_scope(), sfi.abbrev), + ftype, fdisplay, + NULL, 0x0, + wmem_strdup(wmem_epan_scope(), sfi.description), HFILL + } + }; + *ri = finfo; + + if (sfi.is_conversation) { + bi->field_flags[fld_cnt] |= BFF_CONVERSATION; + bi->conversation_filters[conv_fld_cnt].field_info = ri; + bi->conversation_filters[conv_fld_cnt].strbuf = wmem_strbuf_new(wmem_epan_scope(), ""); + + const char *source_name = get_sinsp_source_name(bi->ssi); + const char *conv_filter_name = wmem_strdup_printf(wmem_epan_scope(), "%s %s", source_name, ri->hfinfo.name); + register_log_conversation_filter(source_name, conv_filter_name, is_filter_valid, build_filter, &bi->conversation_filters[conv_fld_cnt]); + if (conv_fld_cnt == 0) { + add_conversation_filter_protocol(source_name); + } + conv_fld_cnt++; + } + + if (sfi.is_info) { + bi->field_flags[fld_cnt] |= BFF_INFO; + } + + if (sfi.type == SFT_STRINGZ && is_addr_field(sfi.abbrev)) { + bi->hf_id_to_addr_id[fld_cnt] = addr_fld_cnt; + + bi->hf_v4_ids[addr_fld_cnt] = -1; + hf_register_info* ri_v4 = bi->hf_v4 + addr_fld_cnt; + hf_register_info finfo_v4 = { + bi->hf_v4_ids + addr_fld_cnt, + { + wmem_strdup_printf(wmem_epan_scope(), "%s (IPv4)", sfi.display), + wmem_strdup_printf(wmem_epan_scope(), "%s.v4", sfi.abbrev), + FT_IPv4, BASE_NONE, + NULL, 0x0, + wmem_strdup_printf(wmem_epan_scope(), "%s (IPv4)", sfi.description), HFILL + } + }; + *ri_v4 = finfo_v4; + + bi->hf_v6_ids[addr_fld_cnt] = -1; + hf_register_info* ri_v6 = bi->hf_v6 + addr_fld_cnt; + hf_register_info finfo_v6 = { + bi->hf_v6_ids + addr_fld_cnt, + { + wmem_strdup_printf(wmem_epan_scope(), "%s (IPv6)", sfi.display), + wmem_strdup_printf(wmem_epan_scope(), "%s.v6", sfi.abbrev), + FT_IPv4, BASE_NONE, + NULL, 0x0, + wmem_strdup_printf(wmem_epan_scope(), "%s (IPv6)", sfi.description), HFILL + } + }; + *ri_v6 = finfo_v6; + addr_fld_cnt++; + } else if (bi->hf_id_to_addr_id) { + bi->hf_id_to_addr_id[fld_cnt] = -1; + } + fld_cnt++; + } + proto_register_field_array(proto_falco_bridge, bi->hf, fld_cnt); + if (addr_fld_cnt) { + proto_register_field_array(proto_falco_bridge, bi->hf_v4, addr_fld_cnt); + proto_register_field_array(proto_falco_bridge, bi->hf_v6, addr_fld_cnt); + } + } +} + +void +import_plugin(char* fname) +{ + nbridges++; + bridge_info* bi = &bridges[nbridges - 1]; + + char *err_str = create_sinsp_source(sinsp_span, fname, &(bi->ssi)); + if (err_str) { + nbridges--; + report_failure("Unable to load sinsp plugin %s: %s.", fname, err_str); + g_free(err_str); + return; + } + + configure_plugin(bi, ""); + + const char *source_name = get_sinsp_source_name(bi->ssi); + const char *plugin_name = g_strdup_printf("%s Plugin", source_name); + bi->proto = proto_register_protocol ( + plugin_name, /* full name */ + source_name, /* short name */ + source_name /* filter_name */ + ); + + static dissector_handle_t ct_handle; + ct_handle = create_dissector_handle(dissect_sinsp_span, bi->proto); + dissector_add_uint("falcobridge.id", bi->source_id, ct_handle); +} + +static void +on_wireshark_exit(void) +{ + // XXX This currently crashes in a sinsp thread. + // destroy_sinsp_span(sinsp_span); + sinsp_span = NULL; +} + +void +proto_register_falcoplugin(void) +{ + proto_falco_bridge = proto_register_protocol ( + "Falco Bridge", /* name */ + "Falco Bridge", /* short name */ + "falcobridge" /* abbrev */ + ); + register_dissector("falcobridge", dissect_falco_bridge, proto_falco_bridge); + + /* + * Create the dissector table that we will use to route the dissection to + * the appropriate Falco plugin. + */ + ptype_dissector_table = register_dissector_table("falcobridge.id", + "Falco Bridge Plugin ID", proto_falco_bridge, FT_UINT32, BASE_DEC); + + /* + * Load the plugins + */ + WS_DIR *dir; + WS_DIRENT *file; + char *filename; + char *spdname = g_build_filename(get_plugins_dir_with_version(), "falco", NULL); + char *ppdname = g_build_filename(get_plugins_pers_dir_with_version(), "falco", NULL); + + /* + * We scan the plugins directory twice. The first time we count how many + * plugins we have, which we need to know in order to allocate the right + * amount of memory. The second time we actually load and configure + * each plugin. + */ + if ((dir = ws_dir_open(spdname, 0, NULL)) != NULL) { + while ((ws_dir_read_name(dir)) != NULL) { + nbridges++; + } + ws_dir_close(dir); + } + + if ((dir = ws_dir_open(ppdname, 0, NULL)) != NULL) { + while ((ws_dir_read_name(dir)) != NULL) { + nbridges++; + } + ws_dir_close(dir); + } + + sinsp_span = create_sinsp_span(); + + bridges = g_new0(bridge_info, nbridges); + nbridges = 0; + + if ((dir = ws_dir_open(spdname, 0, NULL)) != NULL) { + while ((file = ws_dir_read_name(dir)) != NULL) { + filename = g_build_filename(spdname, ws_dir_get_name(file), NULL); + import_plugin(filename); + g_free(filename); + } + ws_dir_close(dir); + } + + if ((dir = ws_dir_open(ppdname, 0, NULL)) != NULL) { + while ((file = ws_dir_read_name(dir)) != NULL) { + filename = g_build_filename(ppdname, ws_dir_get_name(file), NULL); + import_plugin(filename); + g_free(filename); + } + ws_dir_close(dir); + } + + g_free(spdname); + g_free(ppdname); + + /* + * Setup protocol subtree array + */ + static gint *ett[] = { + &ett_falco_bridge, + &ett_sinsp_span, + &ett_address, + }; + + proto_register_field_array(proto_falco_bridge, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + + register_shutdown_routine(on_wireshark_exit); +} + +static bridge_info* +get_bridge_info(guint32 source_id) +{ + for(guint j = 0; j < nbridges; j++) + { + if(bridges[j].source_id == source_id) + { + return &bridges[j]; + } + } + + return NULL; +} + +static int +dissect_falco_bridge(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + col_set_str(pinfo->cinfo, COL_PROTOCOL, "Falco Bridge"); + /* Clear out stuff in the info column */ + col_clear(pinfo->cinfo,COL_INFO); + + // https://github.com/falcosecurity/libs/blob/9c942f27/userspace/libscap/scap.c#L1900 + proto_item *ti = proto_tree_add_item(tree, proto_falco_bridge, tvb, 0, 12, ENC_NA); + proto_tree *fb_tree = proto_item_add_subtree(ti, ett_falco_bridge); + proto_tree_add_item(fb_tree, hf_sdp_source_id_size, tvb, 0, 4, ENC_LITTLE_ENDIAN); + proto_tree_add_item(fb_tree, hf_sdp_lengths, tvb, 4, 4, ENC_LITTLE_ENDIAN); + proto_item *idti = proto_tree_add_item(fb_tree, hf_sdp_source_id, tvb, 8, 4, ENC_LITTLE_ENDIAN); + + guint32 source_id = tvb_get_guint32(tvb, 8, ENC_LITTLE_ENDIAN); + bridge_info* bi = get_bridge_info(source_id); + col_add_fstr(pinfo->cinfo, COL_INFO, "Plugin ID: %u", source_id); + + if (bi == NULL) { + proto_item_append_text(idti, " (NOT SUPPORTED)"); + col_append_str(pinfo->cinfo, COL_INFO, " (NOT SUPPORTED)"); + return tvb_captured_length(tvb); + } + + const char *source_name = get_sinsp_source_name(bi->ssi); + proto_item_append_text(idti, " (%s)", source_name); + col_append_fstr(pinfo->cinfo, COL_INFO, " (%s)", source_name); + + dissector_handle_t dissector = dissector_get_uint_handle(ptype_dissector_table, source_id); + if (dissector) { + tvbuff_t* next_tvb = tvb_new_subset_length(tvb, 12, tvb_captured_length(tvb) - 12); + call_dissector_with_data(dissector, next_tvb, pinfo, tree, bi); + } + + return tvb_captured_length(tvb); +} + +static int +dissect_sinsp_span(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* bi_ptr) +{ + bridge_info* bi = (bridge_info *) bi_ptr; + guint plen = tvb_captured_length(tvb); + const char *source_name = get_sinsp_source_name(bi->ssi); + + col_set_str(pinfo->cinfo, COL_PROTOCOL, source_name); + /* Clear out stuff in the info column */ + col_clear(pinfo->cinfo, COL_INFO); + + proto_item* ti = proto_tree_add_item(tree, bi->proto, tvb, 0, plen, ENC_NA); + proto_tree* fb_tree = proto_item_add_subtree(ti, ett_sinsp_span); + + guint8* payload = (guint8*)tvb_get_ptr(tvb, 0, plen); + + sinsp_field_extract_t *sinsp_fields = (sinsp_field_extract_t*) wmem_alloc(pinfo->pool, sizeof(sinsp_field_extract_t) * bi->visible_fields); + for (uint32_t fld_idx = 0; fld_idx < bi->visible_fields; fld_idx++) { + header_field_info* hfinfo = &(bi->hf[fld_idx].hfinfo); + sinsp_field_extract_t *sfe = &sinsp_fields[fld_idx]; + + sfe->field_id = bi->field_ids[fld_idx]; + sfe->field_name = hfinfo->abbrev; + sfe->type = hfinfo->type == FT_STRINGZ ? SFT_STRINGZ : SFT_UINT64; + } + + // If we have a failure, try to dissect what we can first, then bail out with an error. + bool rc = extract_sisnp_source_fields(bi->ssi, payload, plen, pinfo->pool, sinsp_fields, bi->visible_fields); + + if (!rc) { + REPORT_DISSECTOR_BUG("Falco plugin %s extract error: %s", get_sinsp_source_name(bi->ssi), get_sinsp_source_last_error(bi->ssi)); + } + + for (uint32_t idx = 0; idx < bi->num_conversation_filters; idx++) { + bi->conversation_filters[idx].is_present = false; + wmem_strbuf_truncate(bi->conversation_filters[idx].strbuf, 0); + } + + conversation_element_t *first_conv_els = NULL; // hfid + field val + CONVERSATION_LOG + + for (uint32_t fld_idx = 0; fld_idx < bi->visible_fields; fld_idx++) { + sinsp_field_extract_t *sfe = &sinsp_fields[fld_idx]; + header_field_info* hfinfo = &(bi->hf[fld_idx].hfinfo); + + if (!sfe->is_present) { + continue; + } + + conv_filter_info *cur_conv_filter = NULL; + conversation_element_t *cur_conv_els = NULL; + if ((bi->field_flags[fld_idx] & BFF_CONVERSATION) != 0) { + for (uint32_t cf_idx = 0; cf_idx < bi->num_conversation_filters; cf_idx++) { + if (&(bi->conversation_filters[cf_idx].field_info)->hfinfo == hfinfo) { + cur_conv_filter = &bi->conversation_filters[cf_idx]; + if (!first_conv_els) { + first_conv_els = wmem_alloc0(pinfo->pool, sizeof(conversation_element_t) * 3); + first_conv_els[0].type = CE_INT; + first_conv_els[0].int_val = hfinfo->id; + cur_conv_els = first_conv_els; + } + break; + } + } + } + + + if (sfe->type == SFT_STRINGZ && hfinfo->type == FT_STRINGZ) { + proto_item *pi = proto_tree_add_string(fb_tree, bi->hf_ids[fld_idx], tvb, 0, plen, sfe->res_str); + if (bi->field_flags[fld_idx] & BFF_INFO) { + col_append_sep_fstr(pinfo->cinfo, COL_INFO, ", ", "%s", sfe->res_str); + // Mark it hidden, otherwise we end up with a bunch of empty "Info" tree items. + proto_item_set_hidden(pi); + } + + int addr_fld_idx = bi->hf_id_to_addr_id[fld_idx]; + if (addr_fld_idx >= 0) { + ws_in4_addr v4_addr; + ws_in6_addr v6_addr; + proto_tree *addr_tree; + proto_item *addr_item = NULL; + if (ws_inet_pton4(sfe->res_str, &v4_addr)) { + addr_tree = proto_item_add_subtree(pi, ett_address); + addr_item = proto_tree_add_ipv4(addr_tree, bi->hf_v4_ids[addr_fld_idx], tvb, 0, 0, v4_addr); + set_address(&pinfo->net_src, AT_IPv4, sizeof(ws_in4_addr), &v4_addr); + } else if (ws_inet_pton6(sfe->res_str, &v6_addr)) { + addr_tree = proto_item_add_subtree(pi, ett_address); + addr_item = proto_tree_add_ipv6(addr_tree, bi->hf_v6_ids[addr_fld_idx], tvb, 0, 0, &v6_addr); + set_address(&pinfo->net_src, AT_IPv6, sizeof(ws_in6_addr), &v6_addr); + } + if (addr_item) { + proto_item_set_generated(addr_item); + } + if (cur_conv_filter) { + wmem_strbuf_append(cur_conv_filter->strbuf, sfe->res_str); + cur_conv_filter->is_present = true; + } + if (cur_conv_els) { + cur_conv_els[1].type = CE_ADDRESS; + copy_address(&cur_conv_els[1].addr_val, &pinfo->net_src); + } + } else { + if (cur_conv_filter) { + wmem_strbuf_append_printf(cur_conv_filter->strbuf, "\"%s\"", sfe->res_str); + cur_conv_filter->is_present = true; + } + if (cur_conv_els) { + cur_conv_els[1].type = CE_STRING; + cur_conv_els[1].str_val = wmem_strdup(pinfo->pool, sfe->res_str); + } + } + } + else if (sfe->type == SFT_UINT64 && hfinfo->type == FT_UINT64) { + proto_tree_add_uint64(fb_tree, bi->hf_ids[fld_idx], tvb, 0, plen, sfe->res_u64); + if (cur_conv_filter) { + switch (hfinfo->display) { + case BASE_HEX: + wmem_strbuf_append_printf(cur_conv_filter->strbuf, "%" PRIx64, sfe->res_u64); + break; + case BASE_OCT: + wmem_strbuf_append_printf(cur_conv_filter->strbuf, "%" PRIo64, sfe->res_u64); + break; + default: + wmem_strbuf_append_printf(cur_conv_filter->strbuf, "%" PRId64, sfe->res_u64); + } + cur_conv_filter->is_present = true; + } + + if (cur_conv_els) { + cur_conv_els[1].type = CE_UINT64; + cur_conv_els[1].uint64_val = sfe->res_u64; + } + } + else { + REPORT_DISSECTOR_BUG("Field %s has an unrecognized or mismatched type %u != %u", + hfinfo->abbrev, sfe->type, hfinfo->type); + } + } + + if (!rc) { + REPORT_DISSECTOR_BUG("Falco plugin %s extract error", get_sinsp_source_name(bi->ssi)); + } + + if (first_conv_els) { + first_conv_els[2].type = CE_CONVERSATION_TYPE; + first_conv_els[2].conversation_type_val = CONVERSATION_LOG; + pinfo->conv_elements = first_conv_els; +// conversation_t *conv = find_or_create_conversation(pinfo); +// if (!conv) { +// conversation_new_full(pinfo->fd->num, pinfo->conv_elements); +// } + } + + return plen; +} + +void +proto_reg_handoff_sdplugin(void) +{ +} diff --git a/plugins/epan/falco_bridge/sinsp-span.cpp b/plugins/epan/falco_bridge/sinsp-span.cpp new file mode 100644 index 00000000..eb2fc7e7 --- /dev/null +++ b/plugins/epan/falco_bridge/sinsp-span.cpp @@ -0,0 +1,239 @@ +/* sinsp-span.cpp + * + * By Gerald Combs + * Copyright (C) 2022 Sysdig, Inc. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include <stddef.h> +#include <stdint.h> + +#include <glib.h> + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4100) +#pragma warning(disable:4267) +#endif + +// epan/address.h and driver/ppm_events_public.h both define PT_NONE, so +// handle libsinsp calls here. + +typedef struct hf_register_info hf_register_info; + +typedef struct ss_plugin_info ss_plugin_info; + +#include "sinsp-span.h" + +#include <sinsp.h> + +typedef struct sinsp_source_info_t { + sinsp_plugin *source; + sinsp_evt *evt; + uint8_t *evt_storage; + size_t evt_storage_size; + const char *name; + const char *description; + char *last_error; + const char *fields; +} sinsp_source_info_t; + +typedef struct sinsp_span_t { + sinsp inspector; +} sinsp_span_t; + +sinsp_span_t *create_sinsp_span() +{ + return new(sinsp_span_t); +} + +void destroy_sinsp_span(sinsp_span_t *sinsp_span) { + delete(sinsp_span); +} + +/* + * Populate a source_plugin_info struct with the symbols coming from a library loaded via libsinsp + */ +char * +create_sinsp_source(sinsp_span_t *sinsp_span, const char* libname, sinsp_source_info_t **ssi_ptr) +{ + char *err_str = NULL; + sinsp_source_info_t *ssi = new sinsp_source_info_t(); + + try { + auto sp = sinsp_span->inspector.register_plugin(libname); + if (sp->caps() & CAP_EXTRACTION) { + ssi->source = dynamic_cast<sinsp_plugin *>(sp.get()); + } else { + err_str = g_strdup_printf("%s has unsupported plugin capabilities 0x%02x", libname, sp->caps()); + } + } catch (const sinsp_exception& e) { + err_str = g_strdup_printf("Caught sinsp exception %s", e.what()); + } + + std::string init_err; + if (!err_str) { + if (!ssi->source->init("{}", init_err)) { + err_str = g_strdup_printf("Unable to initialize %s: %s", libname, init_err.c_str()); + } + } + + if (err_str) { + delete ssi; + return err_str; + } + + ssi->evt = new sinsp_evt(&sinsp_span->inspector); + ssi->evt_storage_size = 4096; + ssi->evt_storage = (uint8_t *) g_malloc(ssi->evt_storage_size); + ssi->name = strdup(ssi->source->name().c_str()); + ssi->description = strdup(ssi->source->description().c_str()); + *ssi_ptr = ssi; + return NULL; +} + +uint32_t get_sinsp_source_id(sinsp_source_info_t *ssi) +{ + return ssi->source->id(); +} + +const char *get_sinsp_source_last_error(sinsp_source_info_t *ssi) +{ + if (ssi->last_error) { + free(ssi->last_error); + } + ssi->last_error = strdup(ssi->source->get_last_error().c_str()); + return ssi->last_error; +} + +const char *get_sinsp_source_name(sinsp_source_info_t *ssi) +{ + return ssi->name; +} + +const char *get_sinsp_source_description(sinsp_source_info_t *ssi) +{ + return ssi->description; +} + +size_t get_sinsp_source_nfields(sinsp_source_info_t *ssi) +{ + return ssi->source->fields().size(); +} + +bool get_sinsp_source_field_info(sinsp_source_info_t *ssi, size_t field_num, sinsp_field_info_t *field) +{ + if (field_num >= ssi->source->fields().size()) { + return false; + } + + const filtercheck_field_info *ffi = &ssi->source->fields()[field_num]; + + switch (ffi->m_type) { + case PT_CHARBUF: + field->type = SFT_STRINGZ; + break; + case PT_UINT64: + field->type = SFT_UINT64; + break; + default: + field->type = SFT_UNKNOWN; + } + + switch (ffi->m_print_format) { + case PF_DEC: + field->display_format = SFDF_DECIMAL; + break; + case PF_HEX: + field->display_format = SFDF_HEXADECIMAL; + break; + case PF_OCT: + field->display_format = SFDF_OCTAL; + break; + default: + field->display_format = SFDF_UNKNOWN; + } + + g_strlcpy(field->abbrev, ffi->m_name, sizeof(ffi->m_name)); + g_strlcpy(field->display, ffi->m_display, sizeof(ffi->m_display)); + g_strlcpy(field->description, ffi->m_description, sizeof(ffi->m_description)); + + field->is_hidden = ffi->m_flags & EPF_TABLE_ONLY; + field->is_info = ffi->m_flags & EPF_INFO; + field->is_conversation = ffi->m_flags & EPF_CONVERSATION; + + return true; +} + +// The code below, falcosecurity/libs, and falcosecurity/plugins need to be in alignment. +// The Makefile in /plugins defines FALCOSECURITY_LIBS_REVISION and uses that version of +// plugin_info.h. We need to build against a compatible revision of /libs. +bool extract_sisnp_source_fields(sinsp_source_info_t *ssi, uint8_t *evt_data, uint32_t evt_datalen, wmem_allocator_t *pool, sinsp_field_extract_t *sinsp_fields, uint32_t sinsp_field_len) +{ + std::vector<ss_plugin_extract_field> fields; + + // PPME_PLUGINEVENT_E events have the following format: + // | scap_evt header | uint32_t sizeof(id) = 4 | uint32_t evt_datalen | uint32_t id | uint8_t[] evt_data | + + uint32_t payload_hdr[3] = {4, evt_datalen, ssi->source->id()}; + uint32_t tot_evt_len = (uint32_t)sizeof(scap_evt) + sizeof(payload_hdr) + evt_datalen; + if (ssi->evt_storage_size < tot_evt_len) { + while (ssi->evt_storage_size < tot_evt_len) { + ssi->evt_storage_size *= 2; + } + ssi->evt_storage = (uint8_t *) g_realloc(ssi->evt_storage, ssi->evt_storage_size); + } + scap_evt *sevt = (scap_evt *) ssi->evt_storage; + + sevt->ts = -1; + sevt->tid = -1; + sevt->len = tot_evt_len; + sevt->type = PPME_PLUGINEVENT_E; + sevt->nparams = 2; // Plugin ID + evt_data + + memcpy(ssi->evt_storage + sizeof(scap_evt), payload_hdr, sizeof(payload_hdr)); + memcpy(ssi->evt_storage + sizeof(scap_evt) + sizeof(payload_hdr), evt_data, evt_datalen); + ssi->evt->init(ssi->evt_storage, 0); + + fields.resize(sinsp_field_len); + // We must supply field_id, field, arg, and type. + for (size_t i = 0; i < sinsp_field_len; i++) { + fields.at(i).field_id = sinsp_fields[i].field_id; + fields.at(i).field = sinsp_fields[i].field_name; + if (sinsp_fields[i].type == SFT_STRINGZ) { + fields.at(i).ftype = FTYPE_STRING; + } else { + fields.at(i).ftype = FTYPE_UINT64; + } + } + + bool status = true; + if (!ssi->source->extract_fields(ssi->evt, sinsp_field_len, fields.data())) { + status = false; + } + + for (size_t i = 0; i < sinsp_field_len; i++) { + sinsp_fields[i].is_present = fields.at(i).res_len > 0; + if (sinsp_fields[i].is_present) { + if (fields.at(i).ftype == PT_CHARBUF) { + sinsp_fields[i].res_str = wmem_strdup(pool, *fields.at(i).res.str); + } else if (fields.at(i).ftype == PT_UINT64) { + sinsp_fields[i].res_u64 = *fields.at(i).res.u64; + } else { + status = false; + } + } + } + return status; +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/plugins/epan/falco_bridge/sinsp-span.h b/plugins/epan/falco_bridge/sinsp-span.h new file mode 100644 index 00000000..2a474714 --- /dev/null +++ b/plugins/epan/falco_bridge/sinsp-span.h @@ -0,0 +1,80 @@ +/* sinsp-span.h + * + * By Gerald Combs + * Copyright (C) 2022 Sysdig, Inc. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef __SINSP_SPAN_H__ +#define __SINSP_SPAN_H__ + +#include <stdint.h> + +#include <wsutil/wmem/wmem.h> + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct sinsp_source_info_t sinsp_source_info_t; +typedef struct sinsp_span_t sinsp_span_t; + +typedef enum sinsp_field_type_e { + SFT_UNKNOWN, + SFT_STRINGZ, + SFT_UINT64, +} sinsp_field_type_e; + +typedef enum sinsp_field_display_format_e { + SFDF_UNKNOWN, + SFDF_DECIMAL, + SFDF_HEXADECIMAL, + SFDF_OCTAL +} sinsp_field_display_format_e; + +typedef struct sinsp_field_info_t { + sinsp_field_type_e type; + sinsp_field_display_format_e display_format; + char abbrev[64]; // filter name + char display[64]; // display name + char description[1024]; + bool is_hidden; + bool is_conversation; + bool is_info; +} sinsp_field_info_t; + +typedef struct sinsp_field_extract_t { + uint32_t field_id; // in + const char *field_name; // in + sinsp_field_type_e type; // in, out + bool is_present; // out + const char *res_str; // out + uint64_t res_u64; // out +} sinsp_field_extract_t; + +sinsp_span_t *create_sinsp_span(void); +void destroy_sinsp_span(sinsp_span_t *sinsp_span); + +char *create_sinsp_source(sinsp_span_t *sinsp_span, const char* libname, sinsp_source_info_t **ssi_ptr); + +// Extractor plugin routines. +// These roughly match common_plugin_info +uint32_t get_sinsp_source_id(sinsp_source_info_t *ssi); +const char *get_sinsp_source_last_error(sinsp_source_info_t *ssi); +const char *get_sinsp_source_name(sinsp_source_info_t *ssi); +const char* get_sinsp_source_description(sinsp_source_info_t *ssi); +size_t get_sinsp_source_nfields(sinsp_source_info_t *ssi); +bool get_sinsp_source_field_info(sinsp_source_info_t *ssi, size_t field_num, sinsp_field_info_t *field); +bool extract_sisnp_source_fields(sinsp_source_info_t *ssi, uint8_t *evt_data, uint32_t evt_datalen, wmem_allocator_t *pool, sinsp_field_extract_t *sinsp_fields, uint32_t sinsp_field_len); + + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // __SINSP_SPAN_H__ |