Compare commits

..

191 Commits

Author SHA1 Message Date
Andrea Terzolo 319368f1ad style(probe): add a small comment about extents
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-01-18 15:19:56 +01:00
Andrea Terzolo e0066164af fix(probe): extend support for execve flags in old kernels
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-01-18 15:19:56 +01:00
Andrea Terzolo b9ef30b6e8 fix(driver,libscap): add PPM_SC_EXECVEAT as enum field, and as syscall routing table value
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-01-18 14:37:03 +01:00
Andrea Terzolo e81c3cccdb style: fix indentation problems
Signed-off-by: Andrea Terzolo <s276109@studenti.polito.it>
Co-authored-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-01-18 14:37:03 +01:00
Andrea Terzolo c88206cdff update(libsinsp): adding support to execveat in libsinsp
Signed-off-by: Andrea Terzolo <s276109@studenti.polito.it>
2022-01-18 14:37:03 +01:00
Andrea Terzolo 59d09bcc55 update(libscap): adding support to execveat in libscap
Signed-off-by: Andrea Terzolo <s276109@studenti.polito.it>
2022-01-18 14:37:03 +01:00
Andrea Terzolo 793f1fce63 new(driver/bpf): adding support to execveat in the bpf probe
Signed-off-by: Andrea Terzolo <s276109@studenti.polito.it>
2022-01-18 14:37:03 +01:00
Andrea Terzolo 4ce0fbc999 new(driver): adding support to execveat in the kernel module
Signed-off-by: Andrea Terzolo <s276109@studenti.polito.it>
2022-01-18 14:37:03 +01:00
Lorenzo Susini 0856750ec2 update(driver/bpf): add eBPF filler for mprotect parameters
Signed-off-by: Lorenzo Susini <susinilorenzo1@gmail.com>
2022-01-18 10:33:58 +01:00
Lorenzo Susini 2b9bb1b0e3 update(driver): add mprotect syscall parameters
Signed-off-by: Lorenzo Susini <susinilorenzo1@gmail.com>
2022-01-18 10:33:58 +01:00
Luca Guerra 8f3c9cd67f udig: add fourth argument placeholder for PPME_PROCEXIT_1_E
Signed-off-by: Luca Guerra <luca@guerra.sh>
Co-authored-by: Lorenzo Susini <susinilorenzo1@gmail.com>
2022-01-17 18:24:43 +01:00
Andrea Terzolo 880adec1d2 fix(probe): fix clang optimization problem
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
Co-authored-by: Federico Di Pierro <nierro92@gmail.com>
Co-authored-by: Luca Guerra <luca.guerra@sysdig.com>
2022-01-17 17:10:20 +01:00
Grzegorz Nosek 5f457a3669 fix: Include exe_writable in the length of dumped threadinfos
If threads_from_sinsp is set when creating the capture file,
the thread list is generated by libsinsp. Calculating the length
of each entry is the responsibility of the caller
of `scap_write_proclist_header` and this we need to update both
callers.

Signed-off-by: Grzegorz Nosek <grzegorz.nosek@sysdig.com>
2022-01-17 16:21:07 +01:00
Jason Dellaluce 4de7ad2857 chore(libsinsp): improve UX of plugin init schema validation
This improve the UX of the init config schema automatic validation as follows:
- Parse the schema before the config string. This way, users will not get confused
  about errors coming from their configuration if the plugin has development issues.
- Stubbing empty configurations as empty json objects. This way, a default JSON value
  is always provided, and is possible to pass empty configs to plugins with all-optional
  schemas.

Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-01-17 13:12:11 +01:00
Federico Di Pierro fbfb43da01 fix(driver,userspace/libscap): multiple fixes for syscalls_of_interest feature.
First of all, properly add PPM_SC_UMOUNT2 to routing table and PPM_SC_EXECVE entirely (both as enum field, and as syscall routing table value).
The routing table is only used for PPME_GENERIC_{X,E} events thus it does not really matter to add them (as they're mapped to specific events).

Moreover, cleaned up ia32 routing table a bit removing some duplicated entries.

Finally, avoid breaking ppm_sc_of_interest routing table loop in scap_open_live_int() as multiple syscalls can be mapped to same PPM_SC code.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-01-13 16:51:33 +01:00
Luca Guerra 2ca704d08d driver: use file_inode()
Signed-off-by: Luca Guerra <luca@guerra.sh>
2022-01-13 11:36:26 +01:00
Mark Stemm a9964b82b4 Update plugin api (field properties changes)
Bump the plugin api to reflect the change about plugin field
properties being an array instead of a string.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-01-13 10:33:41 +01:00
Mark Stemm fc717dc6fc Skip fields that have the EPF_TABLE_ONLY flag.
This results them not being printed in --list outputs.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-01-13 10:33:41 +01:00
Mark Stemm e3bd391be9 Parse field properties + honor "hidden" property
Actually parse the "properties" property of field descriptions that
come back from plugin_get_fields(). The acceptable values are any of
"hidden", "conversation", or "info". Only "hidden" actually has
meaning to this plugin framework, and sets the EPF_TABLE_ONLY flag of
the filtercheck that gets created.

Also, when converting a list of filterchecks to a list of
filter_fieldclass_info structs, if the EPF_TABLE_ONLY flag is set, add
a (string) tag "EPF_TABLE_ONLY".

Follow-on commits will honor this tag and not display the field.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-01-13 10:33:41 +01:00
Luca Guerra eb5cf7a17e driver: add double check on f_inode
Signed-off-by: Luca Guerra <luca@guerra.sh>
2022-01-13 10:03:52 +01:00
Luca Guerra 56c348bc2d fix(driver): add ppm_get_mm_exe_file to abstract differences between kernel versions
Signed-off-by: Luca Guerra <luca@guerra.sh>
2022-01-13 10:03:52 +01:00
Jason Dellaluce e25e44b3ba update(libsinsp): support init config schema and automatic validation
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-01-13 09:32:51 +01:00
Jason Dellaluce aabd3cd7f2 update(build): add valijson dependency in libsinsp
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-01-13 09:32:51 +01:00
Jason Dellaluce 05b9658594 update(libscap): add get_init_schema symbols to plugin_info
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-01-13 09:32:51 +01:00
Federico Di Pierro e82451dc0d update(OWNERS): add Federico Di Pierro (fededp) as libs owner and reviewer.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-01-12 19:17:35 +01:00
Jason Dellaluce 6589494b96 update(libsinsp): implement list_open_params() helper for source plugins
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-01-12 10:32:34 +01:00
Jason Dellaluce 00439c5f6a update(libscap): add list_open_params to plugin_info.h
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-01-12 10:32:34 +01:00
Bernhard M. Wiedemann 327d90b271 Fix compilation on i586
without this patch, compilation failed with
ERROR: modpost: "__umoddi3" [/home/abuild/rpmbuild/BUILD/sysdig-0.27.0/build/driver/src/sysdig-probe.ko] undefined!

sysdig-CLA-1.0-signed-off-by: Bernhard M. Wiedemann <bwiedemann@suse.de>

Signed-off-by: Bernhard M. Wiedemann <bwiedemann@suse.de>
2022-01-11 17:14:40 +01:00
Jason Dellaluce f82562cc62 fix(libsinsp): catch errors during plugin initialization
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-01-10 18:53:01 +01:00
Grzegorz Nosek b885b42a7b (fix) Use euid/egid instead of uid/gid in scap_proc_fill_exe_writable
Since we're switching the *effective* uid and gid, we should switch
back to the original *effective* uid and gid as well, not the *real*
one.

Signed-off-by: Grzegorz Nosek <grzegorz.nosek@sysdig.com>
2022-01-10 09:33:44 +01:00
Joseph Pittman 9f15488ab6 Support ARM processor: use proper x86 #ifdefs to isolate x86-specific code
Signed-off-by: Joseph Pittman <joseph.pittman@sysdig.com>
2022-01-04 18:32:40 +01:00
Luca Guerra 9c942f2781 fix(libscap): perform seteuid/setegid only for the current thread
Signed-off-by: Luca Guerra <luca@guerra.sh>
2022-01-03 19:06:33 +01:00
Luca Guerra 7e832f61a0 fix(libscap): use faccessat instead of euidaccess to improve portability
Signed-off-by: Luca Guerra <luca@guerra.sh>
2021-12-23 11:03:25 +01:00
Mark Stemm bb9bee8e52 Move gen_event filterchecks next to event filterchecks in def list
This ensures that they will be displayed next to each other when
printing all fields.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-12-22 20:39:14 +01:00
Mark Stemm c458dcbaae Use filter_fieldclass_info::as_string to print fields
list_fields() is used by programs that use the falcosecurity libs but
are not falco, with its strong notion of event types.

Replace the old version, which used printf and character-by-character
iteration over line strings, with using
sinsp_filter_factory::check_infos_to_fieldclass_infos to get a generic
representation of field classes, and
filter_fieldclass_info::as_string to format the field class info as a
string.

markdown output is now in a dedicated (static) function list_fields_markdown().

This allows for a consistent display of field info across programs.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-12-22 20:39:14 +01:00
Mark Stemm a592aed8b6 Add cleaner built-in way to display filterclass field infos
Add methods to filter_fieldclass_info to return a pretty-printed
representation of the field class, as a string. The output looks like
the following:

-------------------------------
Field Class:                  evt (All event types)
Description:                  These fields can be used for all event types
Event Sources:                aws_cloudtrail syscall

evt.num                       (Type: UINT64) event number.
...

Internally, it uses stringstreams to tokenize long strings like the
description, etc, and wrap them as needed. It also cleans up the
output so words aren't broken across lines.

It has options to print verbose information (adding Types for each
field), and if a set of event sources was provided, those are printed
as well.

This will allow the --list outputs of falco, as well as other programs
that use the libs, to have a single representation of fields.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-12-22 20:39:14 +01:00
Mark Stemm 9ca7f8e3d1 Add convien. method to convert filter check list to fieldclass list
Add a convienence method
sinsp_filter_factory::check_infos_to_fieldclass_infos() that converts
from a vector<filter_check_info> (e.g. filterchecks specifically for
sinsp events) to a list<filter_fieldclass_info> (e.g. description of
fields for all event types).

This makes it easier to have a single method to print info about
filtercheck fields.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-12-22 20:39:14 +01:00
Mark Stemm 1ceff3cc62 Skip potential duplicates when adding to filter check list
When plugins are loaded for programs that don't use event types, you
might end up with duplicate sinsp_filter_check_gen_event objects being
added to the g_filterlist filter_check_list.

This isn't harmful, but it results in duplicate entries when listing
the full set of filter fields.

Handle this by checking to see if there's already a filter_check
object with the same name (e.g. "evt") and shortdesc (e.g. "All event
types"). If there's a duplicate, simply delete the to-be-added object.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-12-22 20:39:14 +01:00
Mark Stemm d2fc128af6 Add additional info to field class structs
Add additional info to filter_field_info:

- shortdesc: a small (3-5 word) description of the field class,
  suitable for printing on the same line as the field class without
  line wrapping.
- data_type: the data type the filtercheck field works
  with (e.g. CHARBUF, UINT64, IPADDR, etc).
- tags: a set of free-form tags for the field. Examples include
  "FILTER ONLY", IDX_REQUIRED, etc.

This will make it easier to print rich information about each field.

Also fill in shortdesc for selected filtercheck objects to help
provide context when printing fields.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-12-22 20:39:14 +01:00
Mark Stemm fce46c38ba Move evt.pluginname/evt.plugininfo into sinsp_filter_check_gen_event
Previously, as plugins were loaded, each plugin would create two
filtercheck objects:

 - A sinsp_filter_check_plugininfo object that defined the fields
   evt.pluginname/evt.plugininfo, with an implementation that only
   returned values for events coming from that specific plugin.
 - A sinsp_filter_check_plugin object that defined the fields exported
   by the plugin.

This made interpreting the output of sysdig -l/falco --list confusing,
as you would get multiple sections in the output for the
almost-duplicate filterchecks.

Fix this by moving evt.plugininfo/evt.pluginname into
sinsp_filter_check_gen_event and looking up the plugin object on the
fly. This avoids the almost-duplicate filtercheck objects, which makes
it easier to print a clean list of fields, as these fields are only
defined in one filtercheck object.

Also, *only* create a sinsp_filter_check_gen_event object for source
plugins. There isn't any need to create one for extractor plugins,
which don't work directly with event meta-data like event number,
timestamp, etc.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-12-22 20:39:14 +01:00
Angelo Puglisi 6329153280 chore(libscap): change missed int32_t to ss_plugin_rc
Signed-off-by: Angelo Puglisi <angelopuglisi86@gmail.com>
2021-12-21 21:13:23 +01:00
Federico Di Pierro 77830a7994 chore(userspace/libscap,userspace/libsinsp): moved back plugin version defines to scap plugin_info.h.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>

Co-authored-by: Mark Stemm <mark.stemm@gmail.com>
2021-12-21 19:11:48 +01:00
Federico Di Pierro b0a0272baf update(userspace/libscap,userspace/libsinsp): dropped plugin api version defines from libscap and moved all the logic to libsinsp.
This is way better because it is libsinsp that does all the checks indeed.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>

Co-authored-by: Mark Stemm <mark.stemm@gmail.com>
2021-12-21 19:11:48 +01:00
Federico Di Pierro f3962b0b7c new(userspace/libsinsp): properly semver check required plugin api version vs framework's one.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-12-21 19:11:48 +01:00
Federico Di Pierro 907c8b92ae new(userspace/libscap,userspace/libsinsp): added scap_get_plugin_api_version() function.
Moreover, provide sinsp wrappers: get_plugin_api_version() and get_plugin_api_version_str().

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-12-21 19:11:48 +01:00
Lorenzo Susini 2b2d06bf64 update(libsinsp/test): add token_bucket unit test to the unit-test-libsinsp executable
Signed-off-by: Lorenzo Susini <susinilorenzo1@gmail.com>
2021-12-21 17:27:33 +01:00
Lorenzo Susini 5dceb60f22 update(libsinsp/test): reintroduce token_bucket unit test, ported to gtest
Signed-off-by: Lorenzo Susini <susinilorenzo1@gmail.com>
2021-12-21 17:27:33 +01:00
Federico Di Pierro 90421457af fix(userspace/libscap): do not return any value from void scap_fseek() function.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-12-21 16:10:28 +01:00
Luca Guerra 4956904cc8 add is_exe_writable
Signed-off-by: Luca Guerra <luca@guerra.sh>
2021-12-21 10:03:28 +01:00
Federico Di Pierro c41b362c5c new(userspace/libscap): properly add scap_bpf_handle_event_mask() internal API to support sinsp::(un)set_eventmask() API on eBPF too.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-12-20 19:07:12 +01:00
Federico Di Pierro 43856ef7e5 update(userspace/libscap): support simpledriver mode for eBPF too.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-12-20 19:07:12 +01:00
Federico Di Pierro ddecc376dc update(driver/kmod): added per-consumer g_events_mask support.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>

Co-authored-by: Grzegorz Nosek <grzegorz.nosek@sysdig.com>
2021-12-20 19:07:12 +01:00
Federico Di Pierro 11ae19d7eb new(driver/bpf,driver/kmod,userspace/libscap,userspace/libsinsp): optimize simpleconsumer mode by filtering syscalls kernel side, for both eBPF and kmod.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>

Co-authored-by: Grzegorz Nosek <grzegorz.nosek@sysdig.com>
Co-authored-by: Mark Stemm <mark.stemm@gmail.com>
2021-12-20 19:07:12 +01:00
Jason Dellaluce ad0655a899 update(libscap): optimize scap reader with function inlining
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
Co-authored-by: Federico Di Pierro <nierro92@gmail.com>
2021-12-16 17:24:59 +01:00
Jason Dellaluce 34e4df705b fix(libscap): restore support for concatenated scap files containing syscall events
Recently, concatenated scap files support has been extended to read dump files containing plugin
events too. However, this apparently broke the support to concatenated files containing syscall events.
This is now fixed by using the refactorings from the previous commits.

Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
Co-authored-by: Federico Di Pierro <nierro92@gmail.com>
2021-12-16 17:24:59 +01:00
Jason Dellaluce 1818aca3ea refactor(libsinsp): handle capture restart and avoid using seek during state initialization
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
Co-authored-by: Federico Di Pierro <nierro92@gmail.com>
2021-12-16 17:24:59 +01:00
Jason Dellaluce cb3706869d update(libscap): adding scap_reader type and primitives
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
Co-authored-by: Federico Di Pierro <nierro92@gmail.com>
2021-12-16 17:24:59 +01:00
Mujahid-Dandoti b573c6e620 Updated protobuf version for s390x
Signed-off-by: Mujahid-Dandoti <Mujahid.Dandoti@ibm.com>
2021-12-15 14:12:02 +01:00
Grzegorz Nosek 3cc178e060 Make the `scap-driver` component name configurable
This affects downstream packaging (the cmake component name
ends up in package names) so make this configurable instead
of forcing `scap-driver`.

Signed-off-by: Angelo Puglisi <angelopuglisi86@gmail.com>
2021-12-13 19:13:33 +01:00
Federico Di Pierro 3ee8453b2d update(userspace/libscap, userspace/libsinsp): addressed review comments.
Avoid 2-pass scan of users and groups.
Fixed up some small issues.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>

Co-authored-by: Grzegorz Nosek <grzegorz.nosek@sysdig.com>
2021-12-10 09:30:06 +01:00
Federico Di Pierro f522165c91 fix(userspace/libsinsp): added back assert() that was removed last commit, plus add another assert() still in sinsp_filter_check_user::extract() after get_user() call.
On a second thought, it is better to leave them here as they alert that something is weird, and they only work for debug builds.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-12-10 09:30:06 +01:00
Federico Di Pierro c954cc3057 refactor(userspace/libsinsp): added a sinsp::get_group() api, similar to sinsp::get_user(), and use it where needed.
Moreover, drop an useless assert() in sinsp_filter_check_user::extract().

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-12-10 09:30:06 +01:00
Federico Di Pierro 8a1db1f21d repo(.gitignore): updated gitignore adding .idea folder (for CLion users)
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-12-10 09:30:06 +01:00
Federico Di Pierro 0fbdd13947 fix(userspace/libscap): improve users loading code robustness: we need 2 passes to load users: a first scan to count number of users, and a second scan to actually fill our data structures.
Previously, if any user was added in between these 2 steps, we would've segfaulted. Now we will "lose" the new user but we won't crash.
While this may seem an unnecessary check, there may be cases (even if never reported) where eg: falco is run as a systemd service and starts very soon during boot up, and some other systemd unit running in parallel is creating some users.
The same fix applies to groups loading code too.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-12-10 09:30:06 +01:00
Loris Degioanni 2160111cd0 make sure curl is actually not include for minimal build
Signed-off-by: Loris Degioanni <loris@sysdig.com>
2021-12-06 14:14:00 +01:00
Loris Degioanni b0a30dc957 cleanups
Signed-off-by: Loris Degioanni <loris@sysdig.com>
2021-12-06 14:14:00 +01:00
Loris Degioanni 14e496c9b4 apple check not needed when detecting strlcpy in the makefile
Signed-off-by: Loris Degioanni <loris@sysdig.com>
2021-12-06 14:14:00 +01:00
Loris Degioanni c432afad7b cleanups
Signed-off-by: Loris Degioanni <loris@sysdig.com>
2021-12-06 14:14:00 +01:00
Loris Degioanni c6cbde657e don't use the OS strlcpy on Mac
Signed-off-by: Loris Degioanni <loris@sysdig.com>
2021-12-06 14:14:00 +01:00
Loris Degioanni 41ae5b776b make sure curl is not used in the minimal build (it also needs to be excluded under Windows and Mac
Signed-off-by: Loris Degioanni <loris@sysdig.com>
2021-12-06 14:14:00 +01:00
Loris Degioanni 597cb94449 when calling sinsp::get_thread_ref(), covert the return value from a shared_ptr to a sinsp_threadinfo* using the shared_ptr get() method. This avoids strange behaviors with some compilers.
Signed-off-by: Loris Degioanni <loris@sysdig.com>
2021-12-06 14:14:00 +01:00
Loris Degioanni 51403ef589 fixes to make the libs compile on Mac
Signed-off-by: Loris Degioanni <loris@sysdig.com>
2021-12-06 14:14:00 +01:00
Federico Di Pierro 8808a06165 fix(build): check GRPC_INCLUDE_IS_GRPCPP macro using GRPC_INCLUDE as path hint.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>

Co-authored-by: Grzegorz Nosek <grzegorz.nosek@sysdig.com>
2021-11-30 13:21:40 +01:00
Federico Di Pierro d1a2dd9bcd Update cmake/modules/grpc.cmake
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>

Co-authored-by: Grzegorz Nosek <grzegorz.nosek@sysdig.com>
2021-11-30 13:21:40 +01:00
Federico Di Pierro c489384e7a update(build): fallback to manually find libraries when grpc config cmake module is not found on system.
Ubuntu focal is not shipping the module. Avoid breaking CI and builds on it.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-11-30 13:21:40 +01:00
Federico Di Pierro a89234bad2 fix(build): update grpc cmake module to use find_package() when building using system library.
This fixes the linking of libraries at the end of Falco build.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>

Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
2021-11-30 13:21:40 +01:00
Federico Di Pierro 99e9a3bafa fix(driver): small fix for eBPF probe on amznlinux2 kernels.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-11-25 15:19:11 +01:00
Grzegorz Nosek de277bbc1e fix(build): Use correct package name in DKMS config file
Commit d3a3d5bf0f renamed
PACKAGE_NAME to DRIVER_PACKAGE_NAME. While doing so, it missed
the template substitution in driver/dkms.conf.in

Signed-off-by: Grzegorz Nosek <grzegorz.nosek@sysdig.com>
2021-11-19 11:33:10 +01:00
Mark Stemm 44a457d8ef Define __STDC_FORMAT_MACROS for inttypes.h (older g++)
When including inttypes.h, define __STDC_FORMAT_MACROS to ensure that
the PRIu32 defines are included.

With newer g++ versions this happens automatically, but with older g++
versions, it's still required.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-11-18 14:29:18 +01:00
Leonardo Grasso a743aa4ab2 refactor: correct var naming
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2021-11-17 23:06:23 +01:00
Leonardo Grasso d3a3d5bf0f build: correct driver package name
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2021-11-17 23:06:23 +01:00
Leonardo Grasso 9f6b4f2da5 chore(userspace): clean up comments
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2021-11-17 23:06:23 +01:00
Leonardo Grasso 06b36b7bb2 docs(userspace/libsinsp): update doxygen headers
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2021-11-17 23:06:23 +01:00
Leonardo Grasso 740f36514b build(userspace/libscap): `PROBE_BPF_FILEPATH` cmake var
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2021-11-17 23:06:23 +01:00
Leonardo Grasso 9c5422657e chore(userspace/libscap): generate module name from `PROBE_NAME`
Co-authored-by: Grzegorz Nosek <grzegorz.nosek@sysdig.com>
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2021-11-17 23:06:23 +01:00
Leonardo Grasso 081f4b8f40 build(userspace/libscap): `SCAP_BPF_PROBE_ENV_VAR_NAME` cmake var
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2021-11-17 23:06:23 +01:00
Leonardo Grasso dfd4f8b442 fix(userspace/libscap): correct SCAP_HOST_ROOT_ENV_VAR_NAME defalt value
Co-authored-by: Grzegorz Nosek <grzegorz.nosek@sysdig.com>
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2021-11-17 23:06:23 +01:00
Leonardo Grasso 3d3445e13c update: rename project to "falcosecurity-libs"
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2021-11-17 23:06:23 +01:00
Leonardo Grasso 0b30d1eb8e update(userspace/libscap): make probe naming more configurable, set defaults to "scap"
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2021-11-17 23:06:23 +01:00
Leonardo Grasso 2c4f5f45c9 chore(userspace/libscap): applying patch from Falco
This patch was originally used in Falco to adapt probe naming.
Reference: 1653898f4f/cmake/modules/falcosecurity-libs-repo/patch/libscap.patch

Co-authored-by: Lorenzo Fontana <lo@linux.com>
Co-Authored-By: Leonardo Di Donato <leodidonato@gmail.com>
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2021-11-17 23:06:23 +01:00
Federico Di Pierro 4f320f32ef update(build): updated luaJIT to most recent version.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-11-17 11:10:23 +01:00
Federico Di Pierro e586e82795 update(build): updated luajit to v2.1.0-beta3.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-11-17 11:10:23 +01:00
Federico Di Pierro c5a8d93162 update(userspace/libsinsp): refactored get_all_data().
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-11-16 18:29:20 +01:00
Federico Di Pierro c48853e379 fix(build): avoid quotation marks.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-11-16 18:29:20 +01:00
Federico Di Pierro 824747a489 update(build): define OPENSSL_LIBRARIES even when built with bundled deps.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-11-16 18:29:20 +01:00
Federico Di Pierro fe4bb4a32e update(userspace/libsinsp): fixed spacing.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>

Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
2021-11-16 18:29:20 +01:00
Federico Di Pierro 4681c3865e update(userspace/libsinsp): fixed spacing.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>

Co-authored-by Leonardo Grasso <me@leonardograsso.com>
2021-11-16 18:29:20 +01:00
Federico Di Pierro 00513d0fee fix(build): updated openssl to 1.1.1l.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-11-16 18:29:20 +01:00
Federico Di Pierro 80ed029a8d fix(build): bump openssl version back to 1.1.1k.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-11-16 18:29:20 +01:00
Federico Di Pierro a358c86b1e fix(userspace/libsinsp): split get_all_data() function in secure() and unsecure().
For SSL one, actually cycle on SSL_read() until all data is received by checking SSL_get_error(), instead of using ioctl().
Unsecure one is left untouched.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>

Co-authored-by: Jason Dellaluce <jasondellaluce@gmail.com>
Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
Co-authored-by: lucklypse <lucklypse@gmail.com>
2021-11-16 18:29:20 +01:00
Federico Di Pierro 7906f7ec41 fix(build): switch gtest to use main branch instead of master.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-11-16 14:27:31 +01:00
lucklypse 3923969d59 cleanup(libsinsp): md format cleanup
Signed-off-by: lucklypse <lucklypse@gmail.com>
2021-11-15 18:08:56 +01:00
lucklypse c93acc5c16 new(libsinsp): change markdown format, introduce markdown for events
Signed-off-by: lucklypse <lucklypse@gmail.com>
2021-11-15 18:08:56 +01:00
lucklypse 4b12f65166 change display order for filters
Signed-off-by: lucklypse <lucklypse@gmail.com>
2021-11-15 18:08:56 +01:00
lucklypse 5d7d9b1bc7 new(libsinsp): add description strings for filter checks
Signed-off-by: lucklypse <lucklypse@gmail.com>
2021-11-15 18:08:56 +01:00
lucklypse ad595e5635 new(sinsp): add description field to field classes
Signed-off-by: lucklypse <lucklypse@gmail.com>
2021-11-15 18:08:56 +01:00
lucklypse 86b931d578 cleanup(libsinsp): add ipaddr and ipnet, prevent assert in debug mode
Signed-off-by: lucklypse <lucklypse@gmail.com>
2021-11-15 18:08:56 +01:00
Federico Di Pierro 96e62087cd Restore back local "bpf_common.h"
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-11-15 17:20:44 +01:00
Federico Di Pierro aaea60e417 feat(userspace/libscap): properly freeze eBPF const maps after creation, where BPF_MAP_FREEZE cmd is available.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-11-15 17:20:44 +01:00
Federico Di Pierro 89e4b20fa8 update(userspace/libscap): updated libscap bpf compat header.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-11-15 17:20:44 +01:00
Federico Di Pierro 941b051e05 fix(driver/bpf): use bpf_ktime_get_boot_ns() where available to fix eBPF events timestamp, fallbacking at current bpf_ktime_get_ns() where not available.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-11-15 17:20:44 +01:00
Jason Dellaluce 1ed3e2a15d update(driver): minor changes on openat2 support
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2021-11-15 14:01:20 +01:00
Jason Dellaluce 17e55b2019 fix(driver): fix driver issues for openat2
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>

Co-authored-by: Federico Di Pierro <nierro92@gmail.com>
2021-11-15 14:01:20 +01:00
Jason Dellaluce 96320d10f0 update(driver): Add openat2 syscall
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2021-11-15 14:01:20 +01:00
Federico Di Pierro 2258aba1b3 fix(driver): retain backward compatibility and add a new "ret" parameter to procexit, with user-seen exit code.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>

Co-authored-by: Mark Stemm <mark.stemm@gmail.com>
2021-11-15 11:59:17 +01:00
Federico Di Pierro 580adee545 new(driver): correctly use WEXITSTATUS set of macros to retrieve procexit return code.
Moreover, enrich procexit event with more data:
* actual return code
* if signal-killed, signal that killed the proc, otherwise 0
* whether a coredump was created

Added a systype_compat header to define sys/types.h macros, as in kernel space they're not available.
The change is backward compatible as we support adding new params to existing events.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-11-15 11:59:17 +01:00
Leonardo Grasso 340be865d9 fix(libsinsp): correct `MINIMAL_BUILD` gate for docker
`container_engine/docker/async_source.h` includes curl, which is not included in the minimal build.

Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2021-11-12 17:00:57 +01:00
Federico Di Pierro 6e805036b8 fix(driver): use snprintf instead of strncpy.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-11-12 10:12:25 +01:00
Federico Di Pierro cf2527eae7 fix(driver): improve unix_socket_path() API making it more robust.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>

Co-authored-by: lucklypse <lucklypse@gmail.com>
2021-11-12 10:12:25 +01:00
Federico Di Pierro 5e017563ac fix(driver): properly update dest pointer upon unix_socket_path() call.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-11-12 10:12:25 +01:00
Federico Di Pierro f3f41828d6 fix(driver): properly support AF_UNIX abstract socket addresses; see https://man7.org/linux/man-pages/man7/unix.7.html.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-11-12 10:12:25 +01:00
Mark Stemm c7524e8e13 Add Mark Stemm as libs owner/reviewer
I know the code fairly well and would be happy to help out with
reviewing PRs.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-11-11 23:43:28 +01:00
Federico Di Pierro b257f5f65a new(userspace/libsinsp): properly support filtering on rawarg.(rel)path.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-11-11 18:22:21 +01:00
Federico Di Pierro fcffb427b7 fix(userspace/libsinsp): properly use dlerror() instead of errno when catching errors of dlopen.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-11-10 11:49:40 +01:00
Luca Guerra 9d2082aa5a chore(libscap): remove unused variable sh from scap_read_init
Signed-off-by: Luca Guerra <luca@guerra.sh>
2021-11-10 10:56:31 +01:00
Grzegorz Nosek 83f460cc5b Split docker_async_source::parse_docker into helper methods
Signed-off-by: Grzegorz Nosek <grzegorz.nosek@sysdig.com>
2021-11-08 18:47:19 +01:00
Grzegorz Nosek 931f2987e9 Split Linux/Windows docker implementations
They have different interfaces and different implementations,
keeping both under the same name doesn't really avoid
any duplication

Signed-off-by: Grzegorz Nosek <grzegorz.nosek@sysdig.com>
2021-11-08 18:47:19 +01:00
Grzegorz Nosek 8cc4e603ed Move common docker bits to a base class
Now we're ready to make clean OS-specific subclasses.

Signed-off-by: Grzegorz Nosek <grzegorz.nosek@sysdig.com>
2021-11-08 18:47:19 +01:00
Grzegorz Nosek dd1088c38e Move most of docker::resolve to a new method
All the code after the initial docker container detection
is platform-agnostic and can be moved to a base class.

Signed-off-by: Grzegorz Nosek <grzegorz.nosek@sysdig.com>
2021-11-08 18:47:19 +01:00
Grzegorz Nosek 59873aa4ab Pass a complete request to parse_docker_async
We need full control over the request to make this method
suitable for putting in a base class.

Signed-off-by: Grzegorz Nosek <grzegorz.nosek@sysdig.com>
2021-11-08 18:47:19 +01:00
Grzegorz Nosek 4833879759 Move docker_async_source to its own source file
Signed-off-by: Grzegorz Nosek <grzegorz.nosek@sysdig.com>
2021-11-08 18:47:19 +01:00
Grzegorz Nosek 3ef184947e Remove docker_connection::build_request
This is a private implementation detail of the connection
class. Also, this fixes a but where (on Windows) we appended
`?size=true` to the wrong place in the request (to the Host
header, not the URL).

Signed-off-by: Grzegorz Nosek <grzegorz.nosek@sysdig.com>
2021-11-08 18:47:19 +01:00
Grzegorz Nosek 45941a4dbb Move platform-specific Docker-API code to a separate class
All the platform-specific code from docker_async_source is moved
to a new docker_connection class. The interface is almost identical,
so keep a single header file with two separate implementation files
(one for Linux, one for Windows).

Signed-off-by: Grzegorz Nosek <grzegorz.nosek@sysdig.com>
2021-11-08 18:47:19 +01:00
Grzegorz Nosek c15bf45640 Pass docker socket to the async engine separately for each request
Rootless podman containers have multiple sockets (one per user
account), and we don't really want to spawn a separate thread
for each user.

This means that we can no longer use a single CURL handle but need
to create a new one for each individual request.

Signed-off-by: Grzegorz Nosek <grzegorz.nosek@sysdig.com>
2021-11-08 18:47:19 +01:00
Grzegorz Nosek 34b9818a26 Move docker_lookup_request to a separate file
Signed-off-by: Grzegorz Nosek <grzegorz.nosek@sysdig.com>
2021-11-08 18:47:19 +01:00
Grzegorz Nosek 5c1d456320 Pass container_id as a const ref to docker_lookup_request ctor
Signed-off-by: Grzegorz Nosek <grzegorz.nosek@sysdig.com>
2021-11-08 18:47:19 +01:00
Grzegorz Nosek 52d12274ec Fix comparison of `docker_lookup_request`s
Signed-off-by: Grzegorz Nosek <grzegorz.nosek@sysdig.com>
2021-11-08 18:47:19 +01:00
Grzegorz Nosek d6da925579 Rename docker_async_instruction to docker_lookup_request
This should explain the purpose of this class better

Signed-off-by: Grzegorz Nosek <grzegorz.nosek@sysdig.com>
2021-11-08 18:47:19 +01:00
Sachin Kumar Singh 396f2c936a Fix link for libraries contibutions in README.md
Signed-off-by: Sachin Kumar Singh <sachinkumarsingh092@gmail.com>
2021-11-08 09:43:57 +01:00
Federico Di Pierro f7877aaf46 fix(driver,userspace/libsinsp): use new PPME_CONTAINER_JSON_2_ events with large payload; leave old ones untouched to avoid breaking backward compatibility.
This way, new scap files with PPME_CONTAINER_JSON_2_ events cannot  be open by old falco; moreover, new falco can correctly open old PPME_CONTAINER_JSON_ events.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>

Co-authored-by: Mark Stemm <mark.stemm@gmail.com>
2021-11-03 16:18:11 +01:00
Luca Guerra 6210e63799 Set compiler flags before including the libs dirs
Move the check for strlcpy to before including the libs dirs so CFLAGS
is properly set.

Also add some messages.

Signed-off-by: Luca Guerra <luca@guerra.sh>
Co-authored-by: Mark Stemm <mark.stemm@gmail.com>
2021-11-03 15:49:35 +01:00
Kevin Kauffman 1d9e61ebf6 expose API to set track connection status on parser
Signed-off-by: Kevin Kauffman <speedyguy17@gmail.com>
2021-11-03 14:17:29 +01:00
Radu Andries c4eab4e5d0 Do not overwrite imageID if it's already set
In environments using CRI imageID can be recovered in 2 ways,
by reading it from the container info or by doing an extra query.

Due to the extra query not working in gke environments, it's safer
to check whether imageID has already been found instead of
unconditionally overwrite it.

Signed-off-by: Radu Andries <radu.andries@sysdig.com>
2021-11-03 13:37:08 +01:00
Jason Dellaluce cc44a90134 fix: check that field is present before copying it
This caused spurious segmentation faults, because generally there is no guarantee that the plugin sets res_str to NULL if field_present is set to false.

Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2021-11-02 17:33:48 +01:00
Mark Stemm 078d395940 Don't define strlcpy when already defined (musl libc)
Falco has a build variant that uses musl libc, and musl libc already
defines strlcpy, so we don't want strlcpy when compiling with musl
libc.

So check for it at cmake time and if found set HAVE_STRLCPY. And only
include the one in strlcpy.h if HAVE_STRLCPY is not defined.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-11-02 15:58:54 +01:00
Mark Stemm f994f47b27 Fix const warnings in libscap
Now that strings are being returned as const char*, make sure the
variable holding the returned value is const char*.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-11-02 15:16:08 +01:00
Federico Di Pierro e879601cb3 update(driver/ebpf): only build workaround label case for BPF tracing, not kmod: kmod allows jump table usage.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-10-29 14:36:54 +02:00
Federico Di Pierro 39145281ad update(driver/ebpf): added better explanation of switch workaround.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-10-29 14:36:54 +02:00
Federico Di Pierro 6eb1a755a9 fix(driver/ebpf): fixed eBPF issues on clang5:
* bound check state->n_drops_X counters
* moved off_bounded declaration near its usage in bpf_parse_readv_writev_bufs()
* force-disable switch jump table

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-10-29 14:36:54 +02:00
Mark Stemm b9d2fccebf Use const char* / uint8_t* in Plugins API
Now that memory is always either owned by the plugin framework (init
config/open params) or owned by the plugin (data payloads, extracted
strings, demographic info), all char*/uint8_t* values involved in the
api should be const.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-28 14:05:35 +02:00
Federico Di Pierro ae6df6f9e1 new(userspace/libscap): added new EV(F)_BLOCK_TYPE_V2_LARGE block types, that mark a block type with a 4B argument payload size header.
Automatically resize scap handle->m_file_evt_buf to fit largest payload size (starting from 65536, ie: normal syscalls limit).
Use 4B size header for plugins payloads. Syscall driven events still use old block type.
new(userspace/libsinsp): Use 4B size header for container metadata. Switch various len function-local variables to uint32_t instead of uint16_t to deal with possible new size.
Updated event::load_params() to account for new EF_LARGE_PAYLOAD flag.
new(driver): added EF_LARGE_PAYLOAD flag. Updated event_table adding EF_LARGE_PAYLOAD flag for PPME_CONTAINER_JSON_E and PPME_PLUGINEVENT_E.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2021-10-28 13:02:31 +02:00
Mark Stemm a0a27a9a02 Properly set event type in plugin_infos()
Previously the type was checked but not returned in the list of ::info
structs.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-27 15:58:54 +02:00
Mark Stemm 9491484512 Fill in some error string when plugin init fails
If plugin init fails, return some kind of error string rather than
nothing. The init failure is likely before plugin_init completes, so
we can't call get_last_error().

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-27 15:58:54 +02:00
Mark Stemm 1a8bb391e6 Set event types properly for plugin filterchecks
All plugin filterchecks will only work on the plugin event type
PPME_PLUGINEVENT_E, so override the default evttypes() method to
return that event type only.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-27 15:58:54 +02:00
Jason Dellaluce 492b669370 docs: improve plugin exported symbols documentation
Co-authored-by: Luca Guerra <luca@guerra.sh>
Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2021-10-27 15:30:07 +02:00
Jason Dellaluce 804d3d1c46 update: remove next symbol
As discussed with other contributors and proposed by @mstemm, we remove the next exported symbol to present an easier interface to plugin authors.

Co-authored-by: Mark Stemm <mark.stemm@gmail.com>
Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2021-10-27 15:30:07 +02:00
Jason Dellaluce 48e61a59d4 update: bump plugin api version (to 0.2.0)
Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2021-10-27 15:30:07 +02:00
Jason Dellaluce 5ac4b34e5f update: sync plugin_info.h with new memory ownership specifics
Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2021-10-27 15:30:07 +02:00
Jason Dellaluce e326ed9af9 refactor: remove free_mem and avoid freeing plugin-allocated memory
This implements the newly proposed memory ownership model for which the plugin is totally responsible of both allocating and deallocating its own memory.

Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2021-10-27 15:30:07 +02:00
Jason Dellaluce b62ce64fec update: adding field_id to ss_plugin_extract_field
This enables plugin developers to implement more efficient fields lookups during extraction.

Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2021-10-27 15:30:07 +02:00
Luca Guerra ed3b0e2596 libsinsp, libscap: update strlcpy() use, remove redundant checks
Signed-off-by: Luca Guerra <luca@guerra.sh>
2021-10-27 08:47:57 +02:00
Luca Guerra 21588dd885 libsinsp, libscap: use common strlcpy implementation
Signed-off-by: Luca Guerra <luca@guerra.sh>
2021-10-27 08:47:57 +02:00
Luca Guerra 4e76d3f383 userspace: add strlcpy implementation
Signed-off-by: Luca Guerra <luca@guerra.sh>
2021-10-27 08:47:57 +02:00
lucklypse 4ec223c81d update(libsinsp): update tinydir to 1.2.4
Signed-off-by: lucklypse <lucklypse@gmail.com>
2021-10-27 08:47:57 +02:00
Jason Dellaluce 0ccf9cf910 fix: correct close cleanup
This prevents `destroy` from being called in `libsinsp` due to `state` being set to NULL after closing.

Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2021-10-22 16:51:39 +02:00
Mark Stemm 8b45f23f4b Add missing conversion from plugin rc to scap rc
Without this fix, the plugin eof wasn't being interpreted properly as
a scap eof.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-21 22:17:22 +02:00
Mark Stemm b45053da54 Set plugin instance to NULL after close()
Make sure to set the plugin instance handle to NULL after calling
close(). This ensures that close() will only be called once for a
given handle.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-21 22:17:22 +02:00
Mark Stemm 922a9fe46a Fix bug in plugin event size as ppm param
ppm param sizes are always 16 bits, while the size of a plugin event
is 32 bits. So make sure to convert the 32 bit plugin event size to a
uint16_t before memcpy()ing.

Plugin event lengths are currently guaranteed to be less than 64k by
the plugin sdks, although we plan on aligning the
sizes (https://github.com/falcosecurity/falco/issues/1759).

Also clean up the comments/variable names to improve readability.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-21 21:46:19 +02:00
Mark Stemm a9dd4cf62c Divorce plugin rc from scap rc
Completely divorce plugin rc values from scap rc values.

Instead of being int32_t, plugin rc values are a ss_plugin_rc enum and
don't directly relate to SCAP_XXX values.

In a couple of locations in scap.c, plugin rcs do need to be mapped to
scap rcs to reflect opening sources, getting next event. Create a
function plugin_rc_to_scap_rc for that.

Also cleaned up a few other cases where ints were being used instead
of enums (plugin type, return value from next, etc.)

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-20 17:30:38 +02:00
Mark Stemm ca92a8665a Split sinsp_evt_filter_check_event in two, use half in plugins
Split sinsp_evt_filter_check_event in two. (New)
sinsp_evt_filter_check_gen_event has fields for event number and
time. sinsp_evt_filter_check_event has fields for everything else.

Add sinsp_evt_filter_check_gen_event to the filtercheck list for
plugins so filters/conditions can have fields that work on time/event
number.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-20 17:30:38 +02:00
Mark Stemm ca51f17c5f Finish moving token bucket impl from falco to libs
It took a while, but we remembered to finish moving the token_bucket
from falco engine to libs. There were 2 copies for a while.

This brings over one change from the falco copy--to have an optional
timer function.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-20 17:30:38 +02:00
Mark Stemm 92a8e2e24b Allow for alt. list of filterchecks when creating filter/formatter
When creating filter/formatter factories, or when creating an
individual filter/formatter, allow specifying an alternate list of
available filterchecks.

This will allow segregating fields used by plugins, which only work
with a given plugin event, from fields used by syscalls, which work
with the "default" set of filterchecks in g_filterlist.

The sinsp_filter_check_list object is a good match for this, but it
represents both a list of filter_checks and also has the default set
of filterchecks for syscalls baked in.

With the notion of factories, we need the first part but don't want
the second. So make up a base class filter_check_list that has the
first part. sinsp_filter_check_list derives from the base class and
has the built-in filterchecks.

Also move it to its own file so it's possible to include the
declaration without including filterchecks.h.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-20 17:30:38 +02:00
Mark Stemm 97c240636b Move async extraction from plugin framework to plugin-sdk-go
Move the handling of async extraction from the plugin framework to
plugin-sdk-go. All go plugins now export a plugin_extract_fields
function in C. That function either uses the async extraction
interface implemented in plugin-sdk-go, or calls the (synchronous)
extraction functions.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-20 17:30:38 +02:00
Mark Stemm 2cd59ea179 Add a plugin API function to free allocated memory
Add a plugin API function to free any memory allocated by the
plugin. The dynamic symbol is plugin_free_mem(). In create_plugin(),
try to resolve plugin_free_mem first and return an error if it can't
be found. Otherwise, save it in the plugin_info structs.

In other places, where the framework was calling free() call the
resolved symbol instead.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-20 17:30:38 +02:00
Mark Stemm 8ac46c87ef Remove plugin_evt_processor for now
Was future-thinking towards parallel processing of events but we don't
need it for MVP.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-20 17:30:38 +02:00
Mark Stemm 54ab5d2304 New filtercheck field evt.datetime.s
This is like evt.datetime but the the time part skips
nanoseconds. This can be used for events from plugins, which might not
need nanosecond resolution.

Co-authored-by: Loris Degioanni <loris@sysdig.com>
Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-20 17:30:38 +02:00
Mark Stemm 13ba8938f7 libsinsp support for plugins
libsinsp support for plugins involves:

- methods to load a set of plugins and associate them with an
  inspector (add_plugin, get_plugins, get_plugin_by_id,
  get_source_plugin_by_source, etc.)

- methods to open a stream of events from a plugin and check its
  progress (set_input_plugin/set_input_plugin_open_params,
  get_read_progress_plugin, etc.)

- plugin_evt_processor is forward-looking and will allow for parallel
  reading of events from plugins.

Co-authored-by: Loris Degioanni <loris@sysdig.com>
Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-20 17:30:38 +02:00
Mark Stemm f557172cd7 libscap support for source plugins
Add support for opening plugins and sourcing events from plugins in
libscap:

- The structs in plugin_info represent the interface to a plugin
  dynamic library. Required enums/consts used for the plugin interface
  are defined here. The source_plugin_info/extractor_plugin_info
  structs contain the resolved functions.

- scap_open_plugin_int() handles the details of opening a plugin. It
  takes a source_plugin_info struct and calls the plugin's open()
  function.

- scap_next_plugin() calls the plugin's next() method to return
  events.

Capture file handling also has some changes, with the introduction of
plugin sources:

 - When writing capture files from plugin events, the resulting
   capture file does not have any of the following:
      - fd list
      - process list
      - machine info
      - interface list
      - user list

 - When reading capture files, libscap does not mandate any of the
   above blocks, as they could have been written by a libscap reading
   plugin events.

 - When reading capture files, if a section header block is found in
   the middle of the file, instead of returning SCAP_UNEXPECTED_BLOCK,
   read the section header, make no changes, and return
   SCAP_TIMEOUT. This allows a capture file to contain a section
   header block in the middle, which supports use cases involving
   concatenating .scap files from plugins into a single file.

Co-authored-by: Loris Degioanni <loris@sysdig.com>
Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-20 17:30:38 +02:00
Mark Stemm 42921f2cd8 Use CMAKE_CURRENT_SOURCE_DIR for common
This was required when libscap was included by other projects.

Co-authored-by: Loris Degioanni <loris@sysdig.com>
Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-20 17:30:38 +02:00
Mark Stemm 1b175074bf Add new "plugin event" event type
A plugin event has 2 fields:
 - plugin id
 - arbitrary event buffer, defined by the plugin

This allows events created by plugins to live in capture files/be
represented by sinsp_evt objects like all other events.

Co-authored-by: Loris Degioanni <loris@sysdig.com>
Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-20 17:30:38 +02:00
Mark Stemm 769210f4ba Classes to represent a plugin
New classes that represent a plugin/filterchecks for plugins. High
level summary of classes:

sinsp_plugin: base class, an object representing a loaded plugin,
handles the dynamic library loading/function resolving/etc. Includes
the functions that were dlsym()d from the shared library as well as
demographic info (name, desc, contact) read from the plugin via
plugin_get_name(), plugin_get_desc(), etc. Has static methods to
create a plugin object and register it with sinsp.

Note that sinsp_plugin does *not* have any ability to return
events via a next() function. That's handled in libscap.

sinsp_source_plugin: child of sinsp_plugin, has additional methods to
get read progress and display events as a string. Both call the
underlying plugin functions.

sinsp_extractor_plugin: child of sinsp_plugin, has additional methods
to check for a compatible source.

sinsp_async_extractor: handles the framework side of
plugin_register_async_extractor. In extract, updates the shared struct
and waits for a value from the plugin.

sinsp_filter_check_plugininfo: filtercheck class that handles the
fields "evt.pluginname" and "evt.plugininfo". Each object is
associated with a single plugin and the object is created in
sinsp_plugin::register_plugin().

sinsp_filter_check_plugin: filtercheck class that handles all other
fields exported by a plugin. The set of fields are those exported by
the plugin in plugin_get_fields(), and extract calls the plugin's
sinsp_plugin::extract_field() method, which in turn calls
plugin_extract_fields().

Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
Co-authored-by: Loris Degioanni <loris@sysdig.com>
Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-20 17:30:38 +02:00
Grzegorz Nosek 326b87e5d7 RHEL 8 backports actually happened in 8.1, not 8.0
This fixes eBPF build for RHEL 8.0 kernels

Signed-off-by: Grzegorz Nosek <grzegorz.nosek@sysdig.com>
2021-10-18 16:22:50 +02:00
Mark Stemm a03ccfda79 Update tests to reflect skipping deprecated events
Events with EF_OLD_VERSION/EF_UNUSED are skipped by libsinsp, so skip
them here too.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-11 16:05:41 +02:00
Mark Stemm 0a34d5460c Skip deprecated events in evttypes()
When iterating over event numbers (e.g. PPME_EVENT_CONTAINER_{E,X}) to
see if they match a param that has the event number as a
string (e.g. "container"), skip all events that are
old (e.g. EF_UNUSED or EF_OLD_VERSION).

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-11 16:05:41 +02:00
Mark Stemm 7f84dd1f3b Set EF_OLD_VERSION for PPME_CONTAINER_{E,X}
These events are replaced by PPME_CONTAINER_JSON_{E,X}.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-11 16:05:41 +02:00
Mark Stemm 55b2be3d9b Also skip EF_OLD_VERSION events from simple consumer
The event table has a few "old" versions of some events, where the
libs needed new params for an event, and did that by defining a new
event. PPM_SYSCALL_EXECVE_* are good examples, where the parameters
kept changing as we wanted to track additional info during execs.

There's no reason to consider these old events with a simple consumer,
so exclude events with this flag.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-11 16:05:41 +02:00
Mark Stemm ce142f4757 Use error value over lua_error() in lua callbacks
Instead of returning errors in lua callbacks via lua_error(), which
stops the lua interpreter, return the error explicitly as a string
error or nil.

This allows for more graceful error handling on the lua side.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-05 16:44:11 +02:00
Mark Stemm a5ea367368 Add notion of generic formatters/formatter factories
Add the notion of "generic" event formatters and formatter factories
that can create a formatter for a given format string:

- gen_filter.h defines two new classes: gen_event_formatter provides
  the interface to format events:
    - set_format(): set the output format and format string
    - tostring(): resolve the format with info from a gen_event,
      populating a resolved string.
    - tostring_withformat(): like tostring() but with a one-off output
      format.
    - get_field_values(): return all templated field names and values
      from the configured format string.
    - get_output_format(): get the current output format.
- gen_event_formatter_factory performs a similar role as the existing
  sinsp_evt_formatter_cache, in that it maintains a cache of previously
  used format strings/formatters, to avoid the overhead of creating
  formatters. It simply returns a formatter, though, rather than
  duplicating the format methods like sinsp_evt_formatter_cache does.

This can be used in programs like falco to format general purpose
events without having a direct connection to an
inspector/filterchecks/etc.

- The eventformatter changes simply add gen_event_formatter as a
  parent class and implements the interfaces. To aid in backwards
  compatibility with other parts of libsinsp, this only adds new
  methods as needed to conform to the gen_event_formatter
  interface. In some cases, the new methods just call existing methods
  that did the same thing.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-04 17:43:19 +02:00
Mark Stemm 59a81453d8 Add ability to return all fields exported by a factory
Add the ability to return all fields exported by a factory. This is
important for programs like falco that need to validate rule filter
expressions for various event sources, as well as print out sets of
supported fields.

Previously, falco did direct calls to
sinsp::get_filtercheck_fields_info but we're trying to standardize
everything to work through factories, to make it easier to support new
event sources. This PR supports that work.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-04 10:28:50 +02:00
Mark Stemm b5308ce965 Update lua parser to be short-lived for a single filter
Clean up the interface for lua_parser/lua_parser_api so it doesn't
rely on a single object.

Context--the lua_parser object simply holds some intermediate
state (e.g. nesting level) and builds up a gen_filter as the lua side
traverses the ast of a filter expression. The lua_parser_api class
just has static methods that are registered into lua.

lua_parser used to be a single object that was reset for each
filter. Now, it's an object that is created as a single filter is
parsed and deleted afterward. The callbacks always pass the lua_parser
object as a first argument and the state/filter in the object is
updated.

Also, registering the lua callbacks is now done via a static method
instead of in the constructor.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-01 19:21:32 +02:00
Mark Stemm 01f2e2a2fb Add ability to return event types used by a filter
Add the ability to return the event types used by a filter. For
example, if a filter was "evt.type=open and fd.name=/tmp/foo", the
event types would be PPME_SYSCALL_OPEN*_{E,X}.

By default, an empty set is returned, meaning no specific events are
used.

Event types PPME_GENERIC_{E,X} are not included and it's assumed the
code using this will handle those event types directly.

This is used in programs like falco to provide a quick external test
against an event to see if it makes sense to evaluate the filter at
all. This can speed up event processing when falco has a large number
of loaded rules. Prior to this change, this was handled solely in
falco's lua code for loading rules. Moving responsibility to the
filter significantly simplifies the falco side of rule loading.

In the base classes, new methods
gen_event_filter_check::evttypes/possible_evttypes return a set of
event types. The base class implementation just returns a single event
type "1".

gen_event_filter_expression::evttypes() does all the work of iterating
over the filterchecks that make up an expression and combining sets of
event types. possible_evttypes is used for "not" operators, which
invert a set of event types to include everything outside the set.

The sinsp "base" class sinsp_filter_check just returns all event types
from 2 to PPM_EVENT_MAX.

The only actual implementation of evttypes() that does something is in
sinsp_filter_check_event for the field "evt.type". The method handles
=, in, and != as comparison operators.

Also add a unit test that compiles various filters and double-checks
the resulting set of event types.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2021-10-01 18:33:45 +02:00
114 changed files with 9600 additions and 2223 deletions

View File

@ -27,13 +27,12 @@ if(EXISTS ${CMAKE_CURRENT_BINARY_DIR}/CMakeLists.txt)
" 4. Run cmake from the build directory. ex: cmake ..\n" " 4. Run cmake from the build directory. ex: cmake ..\n"
" 5. Run make from the build directory. ex: make\n" " 5. Run make from the build directory. ex: make\n"
"Full paste-able example:\n" "Full paste-able example:\n"
"( rm -f CMakeCache.txt; mkdir build; cd build; cmake ..; make )\n" "( rm -f CMakeCache.txt; mkdir build; cd build; cmake ..; make )")
"The following wiki page has more information on manually building sysdig: http://bit.ly/1oJ84UI")
endif() endif()
cmake_minimum_required(VERSION 2.8.2) cmake_minimum_required(VERSION 2.8.2)
project(sysdig) project(falcosecurity-libs)
option(MINIMAL_BUILD "Produce a minimal build with only the essential features (no eBPF probe driver, no kubernetes, no mesos, no marathon and no container metadata)" OFF) option(MINIMAL_BUILD "Produce a minimal build with only the essential features (no eBPF probe driver, no kubernetes, no mesos, no marathon and no container metadata)" OFF)
option(MUSL_OPTIMIZED_BUILD "Enable if you want a musl optimized build" OFF) option(MUSL_OPTIMIZED_BUILD "Enable if you want a musl optimized build" OFF)
@ -42,15 +41,24 @@ option(MUSL_OPTIMIZED_BUILD "Enable if you want a musl optimized build" OFF)
list(APPEND CMAKE_MODULE_PATH list(APPEND CMAKE_MODULE_PATH
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
if(NOT DEFINED SYSDIG_VERSION) if(NOT DEFINED FALCOSECURITY_LIBS_VERSION)
set(SYSDIG_VERSION "0.1.1dev") set(FALCOSECURITY_LIBS_VERSION "0.1.1dev")
endif() endif()
if(NOT CMAKE_BUILD_TYPE) if(NOT CMAKE_BUILD_TYPE)
SET(CMAKE_BUILD_TYPE Release) SET(CMAKE_BUILD_TYPE Release)
endif() endif()
set(PACKAGE_NAME "sysdig") set(DRIVER_PACKAGE_NAME "scap")
include(CheckSymbolExists)
check_symbol_exists(strlcpy "string.h" HAVE_STRLCPY)
if(HAVE_STRLCPY)
message(STATUS "Existing strlcpy found, will *not* use local definition by setting -DHAVE_STRLCPY.")
add_definitions(-DHAVE_STRLCPY)
else()
message(STATUS "No strlcpy found, will use local definition")
endif()
include(CompilerFlags) include(CompilerFlags)
@ -69,3 +77,4 @@ if(CREATE_TEST_TARGETS AND NOT WIN32)
COMMAND ${CMAKE_MAKE_PROGRAM} run-unit-test-libsinsp COMMAND ${CMAKE_MAKE_PROGRAM} run-unit-test-libsinsp
) )
endif() endif()

4
OWNERS
View File

@ -4,9 +4,13 @@ approvers:
- leogr - leogr
- gnosek - gnosek
- ldegio - ldegio
- mstemm
- fededp
reviewers: reviewers:
- fntlnz - fntlnz
- leodido - leodido
- leogr - leogr
- gnosek - gnosek
- ldegio - ldegio
- mstemm
- fededp

View File

@ -1,3 +1,3 @@
# falcosecurity/libs # falcosecurity/libs
As per the [OSS Libraries Contribution Plan](https://github.com/falcosecurity/falco/blob/master/proposals/2021019-libraries-donation.md), this repository has been chosen to be the new home for **libsinsp**, **libscap**, the **kernel module driver** and the **eBPF driver sources**. As per the [OSS Libraries Contribution Plan](https://github.com/falcosecurity/falco/blob/master/proposals/20210119-libraries-contribution.md), this repository has been chosen to be the new home for **libsinsp**, **libscap**, the **kernel module driver** and the **eBPF driver sources**.

View File

@ -8,31 +8,31 @@ endif()
if(NOT WIN32) if(NOT WIN32)
set(SYSDIG_COMMON_FLAGS "-Wall -ggdb") set(FALCOSECURITY_LIBS_COMMON_FLAGS "-Wall -ggdb")
set(SYSDIG_DEBUG_FLAGS "-D_DEBUG") set(FALCOSECURITY_LIBS_DEBUG_FLAGS "-D_DEBUG")
set(SYSDIG_RELEASE_FLAGS "-O3 -fno-strict-aliasing -DNDEBUG") set(FALCOSECURITY_LIBS_RELEASE_FLAGS "-O3 -fno-strict-aliasing -DNDEBUG")
if(MINIMAL_BUILD) if(MINIMAL_BUILD)
set(SYSDIG_COMMON_FLAGS "${SYSDIG_COMMON_FLAGS} -DMINIMAL_BUILD") set(FALCOSECURITY_LIBS_COMMON_FLAGS "${FALCOSECURITY_LIBS_COMMON_FLAGS} -DMINIMAL_BUILD")
endif() endif()
if(MUSL_OPTIMIZED_BUILD) if(MUSL_OPTIMIZED_BUILD)
set(SYSDIG_COMMON_FLAGS "${SYSDIG_COMMON_FLAGS} -static -Os") set(FALCOSECURITY_LIBS_COMMON_FLAGS "${FALCOSECURITY_LIBS_COMMON_FLAGS} -static -Os")
endif() endif()
if(BUILD_WARNINGS_AS_ERRORS) if(BUILD_WARNINGS_AS_ERRORS)
set(CMAKE_SUPPRESSED_WARNINGS "-Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-type-limits -Wno-implicit-fallthrough -Wno-format-truncation") set(CMAKE_SUPPRESSED_WARNINGS "-Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-type-limits -Wno-implicit-fallthrough -Wno-format-truncation")
set(SYSDIG_COMMON_FLAGS "${SYSDIG_COMMON_FLAGS} -Wextra -Werror ${CMAKE_SUPPRESSED_WARNINGS}") set(FALCOSECURITY_LIBS_COMMON_FLAGS "${FALCOSECURITY_LIBS_COMMON_FLAGS} -Wextra -Werror ${CMAKE_SUPPRESSED_WARNINGS}")
endif() endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SYSDIG_COMMON_FLAGS}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FALCOSECURITY_LIBS_COMMON_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SYSDIG_COMMON_FLAGS} -std=c++0x") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FALCOSECURITY_LIBS_COMMON_FLAGS} -std=c++0x")
set(CMAKE_C_FLAGS_DEBUG "${SYSDIG_DEBUG_FLAGS}") set(CMAKE_C_FLAGS_DEBUG "${FALCOSECURITY_LIBS_DEBUG_FLAGS}")
set(CMAKE_CXX_FLAGS_DEBUG "${SYSDIG_DEBUG_FLAGS}") set(CMAKE_CXX_FLAGS_DEBUG "${FALCOSECURITY_LIBS_DEBUG_FLAGS}")
set(CMAKE_C_FLAGS_RELEASE "${SYSDIG_RELEASE_FLAGS}") set(CMAKE_C_FLAGS_RELEASE "${FALCOSECURITY_LIBS_RELEASE_FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE "${SYSDIG_RELEASE_FLAGS}") set(CMAKE_CXX_FLAGS_RELEASE "${FALCOSECURITY_LIBS_RELEASE_FLAGS}")
if(CMAKE_SYSTEM_NAME MATCHES "Linux") if(CMAKE_SYSTEM_NAME MATCHES "Linux")
add_definitions(-DHAS_CAPTURE) add_definitions(-DHAS_CAPTURE)
@ -41,18 +41,18 @@ if(NOT WIN32)
else() else()
set(MINIMAL_BUILD ON) set(MINIMAL_BUILD ON)
set(SYSDIG_COMMON_FLAGS "-D_CRT_SECURE_NO_WARNINGS -DWIN32 -DMINIMAL_BUILD /EHsc /W3 /Zi") set(FALCOSECURITY_LIBS_COMMON_FLAGS "-D_CRT_SECURE_NO_WARNINGS -DWIN32 -DMINIMAL_BUILD /EHsc /W3 /Zi")
set(SYSDIG_DEBUG_FLAGS "/MTd /Od") set(FALCOSECURITY_LIBS_DEBUG_FLAGS "/MTd /Od")
set(SYSDIG_RELEASE_FLAGS "/MT") set(FALCOSECURITY_LIBS_RELEASE_FLAGS "/MT")
set(CMAKE_C_FLAGS "${SYSDIG_COMMON_FLAGS}") set(CMAKE_C_FLAGS "${FALCOSECURITY_LIBS_COMMON_FLAGS}")
set(CMAKE_CXX_FLAGS "${SYSDIG_COMMON_FLAGS}") set(CMAKE_CXX_FLAGS "${FALCOSECURITY_LIBS_COMMON_FLAGS}")
set(CMAKE_C_FLAGS_DEBUG "${SYSDIG_DEBUG_FLAGS}") set(CMAKE_C_FLAGS_DEBUG "${FALCOSECURITY_LIBS_DEBUG_FLAGS}")
set(CMAKE_CXX_FLAGS_DEBUG "${SYSDIG_DEBUG_FLAGS}") set(CMAKE_CXX_FLAGS_DEBUG "${FALCOSECURITY_LIBS_DEBUG_FLAGS}")
set(CMAKE_C_FLAGS_RELEASE "${SYSDIG_RELEASE_FLAGS}") set(CMAKE_C_FLAGS_RELEASE "${FALCOSECURITY_LIBS_RELEASE_FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE "${SYSDIG_RELEASE_FLAGS}") set(CMAKE_CXX_FLAGS_RELEASE "${FALCOSECURITY_LIBS_RELEASE_FLAGS}")
add_definitions(-DHAS_CAPTURE) add_definitions(-DHAS_CAPTURE)

View File

@ -3,6 +3,30 @@ option(USE_BUNDLED_GRPC "Enable building of the bundled grpc" ${USE_BUNDLED_DEPS
if(GRPC_INCLUDE) if(GRPC_INCLUDE)
# we already have grpc # we already have grpc
elseif(NOT USE_BUNDLED_GRPC) elseif(NOT USE_BUNDLED_GRPC)
# gRPC
find_package(gRPC CONFIG)
if(gRPC_FOUND)
message(STATUS "Using gRPC ${gRPC_VERSION}")
set(GPR_LIB gRPC::gpr)
set(GRPC_LIB gRPC::grpc)
set(GRPCPP_LIB gRPC::grpc++)
# gRPC C++ plugin
get_target_property(GRPC_CPP_PLUGIN gRPC::grpc_cpp_plugin LOCATION)
if(NOT GRPC_CPP_PLUGIN)
message(FATAL_ERROR "System grpc_cpp_plugin not found")
endif()
# gRPC include dir + properly handle grpc{++,pp}
get_target_property(GRPC_INCLUDE gRPC::grpc++ INTERFACE_INCLUDE_DIRECTORIES)
find_path(GRPCXX_INCLUDE NAMES grpc++/grpc++.h PATHS ${GRPC_INCLUDE})
if(NOT GRPCXX_INCLUDE)
find_path(GRPCPP_INCLUDE NAMES grpcpp/grpcpp.h PATHS ${GRPC_INCLUDE})
add_definitions(-DGRPC_INCLUDE_IS_GRPCPP=1)
endif()
else()
# Fallback to manually find libraries;
# Some distro, namely Ubuntu focal, do not install gRPC config cmake module
find_library(GPR_LIB NAMES gpr) find_library(GPR_LIB NAMES gpr)
if(GPR_LIB) if(GPR_LIB)
message(STATUS "Found gpr lib: ${GPR_LIB}") message(STATUS "Found gpr lib: ${GPR_LIB}")
@ -28,6 +52,7 @@ elseif(NOT USE_BUNDLED_GRPC)
if(NOT GRPC_CPP_PLUGIN) if(NOT GRPC_CPP_PLUGIN)
message(FATAL_ERROR "System grpc_cpp_plugin not found") message(FATAL_ERROR "System grpc_cpp_plugin not found")
endif() endif()
endif()
else() else()
include(cares) include(cares)
include(protobuf) include(protobuf)

View File

@ -12,7 +12,7 @@ elseif(NOT USE_BUNDLED_GTEST)
message(FATAL_ERROR "Couldn't find system gtest") message(FATAL_ERROR "Couldn't find system gtest")
endif() endif()
else() else()
# https://github.com/google/googletest/tree/master/googletest#incorporating-into-an-existing-cmake-project # https://github.com/google/googletest/tree/main/googletest#incorporating-into-an-existing-cmake-project
# Download and unpack googletest at configure time # Download and unpack googletest at configure time
configure_file(CMakeListsGtestInclude.cmake ${PROJECT_BINARY_DIR}/googletest-download/CMakeLists.txt) configure_file(CMakeListsGtestInclude.cmake ${PROJECT_BINARY_DIR}/googletest-download/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .

View File

@ -17,13 +17,18 @@ include(ExternalProject)
include(libscap) include(libscap)
if(NOT WIN32) if(NOT WIN32)
include(tbb) include(tbb)
endif()
if(NOT WIN32 AND NOT APPLE)
include(b64) include(b64)
include(jq) include(jq)
endif() endif()
if(NOT WIN32 AND NOT APPLE AND NOT MINIMAL_BUILD)
include(curl)
endif()
include(jsoncpp) include(jsoncpp)
include(valijson)
if(NOT MINIMAL_BUILD) if(NOT MINIMAL_BUILD)
include(cares) include(cares)
include(curl)
endif() endif()
set(LIBSINSP_INCLUDE_DIRS ${LIBSINSP_DIR}/userspace/libsinsp ${LIBSINSP_DIR}/common ${LIBSCAP_INCLUDE_DIRS} ${DRIVER_CONFIG_DIR}) set(LIBSINSP_INCLUDE_DIRS ${LIBSINSP_DIR}/userspace/libsinsp ${LIBSINSP_DIR}/common ${LIBSCAP_INCLUDE_DIRS} ${DRIVER_CONFIG_DIR})
@ -34,17 +39,27 @@ endif()
if(NOT WIN32) if(NOT WIN32)
get_filename_component(TBB_ABSOLUTE_INCLUDE_DIR ${TBB_INCLUDE_DIR} ABSOLUTE) get_filename_component(TBB_ABSOLUTE_INCLUDE_DIR ${TBB_INCLUDE_DIR} ABSOLUTE)
list(APPEND LIBSINSP_INCLUDE_DIRS ${TBB_ABSOLUTE_INCLUDE_DIR}) list(APPEND LIBSINSP_INCLUDE_DIRS ${TBB_ABSOLUTE_INCLUDE_DIR})
endif()
get_filename_component(JSONCPP_ABSOLUTE_INCLUDE_DIR ${JSONCPP_INCLUDE} ABSOLUTE)
list(APPEND LIBSINSP_INCLUDE_DIRS ${JSONCPP_ABSOLUTE_INCLUDE_DIR})
get_filename_component(VALIJSON_ABSOLUTE_INCLUDE_DIR ${VALIJSON_INCLUDE} ABSOLUTE)
list(APPEND LIBSINSP_INCLUDE_DIRS ${VALIJSON_ABSOLUTE_INCLUDE_DIR})
if(NOT MINIMAL_BUILD)
get_filename_component(CARES_ABSOLUTE_INCLUDE_DIR ${CARES_INCLUDE} ABSOLUTE)
list(APPEND LIBSINSP_INCLUDE_DIRS ${CARES_ABSOLUTE_INCLUDE_DIR})
endif()
if(NOT WIN32 AND NOT APPLE)
get_filename_component(B64_ABSOLUTE_INCLUDE_DIR ${B64_INCLUDE} ABSOLUTE) get_filename_component(B64_ABSOLUTE_INCLUDE_DIR ${B64_INCLUDE} ABSOLUTE)
list(APPEND LIBSINSP_INCLUDE_DIRS ${B64_ABSOLUTE_INCLUDE_DIR}) list(APPEND LIBSINSP_INCLUDE_DIRS ${B64_ABSOLUTE_INCLUDE_DIR})
get_filename_component(JQ_ABSOLUTE_INCLUDE_DIR ${JQ_INCLUDE} ABSOLUTE) get_filename_component(JQ_ABSOLUTE_INCLUDE_DIR ${JQ_INCLUDE} ABSOLUTE)
list(APPEND LIBSINSP_INCLUDE_DIRS ${JQ_ABSOLUTE_INCLUDE_DIR}) list(APPEND LIBSINSP_INCLUDE_DIRS ${JQ_ABSOLUTE_INCLUDE_DIR})
endif() endif()
get_filename_component(JSONCPP_ABSOLUTE_INCLUDE_DIR ${JSONCPP_INCLUDE} ABSOLUTE) if(NOT WIN32 AND NOT APPLE AND NOT MINIMAL_BUILD)
list(APPEND LIBSINSP_INCLUDE_DIRS ${JSONCPP_ABSOLUTE_INCLUDE_DIR})
if(NOT MINIMAL_BUILD)
get_filename_component(CARES_ABSOLUTE_INCLUDE_DIR ${CARES_INCLUDE} ABSOLUTE)
list(APPEND LIBSINSP_INCLUDE_DIRS ${CARES_ABSOLUTE_INCLUDE_DIR})
get_filename_component(CURL_ABSOLUTE_INCLUDE_DIR ${CURL_INCLUDE_DIR} ABSOLUTE) get_filename_component(CURL_ABSOLUTE_INCLUDE_DIR ${CURL_INCLUDE_DIR} ABSOLUTE)
list(APPEND LIBSINSP_INCLUDE_DIRS ${CURL_ABSOLUTE_INCLUDE_DIR}) list(APPEND LIBSINSP_INCLUDE_DIRS ${CURL_ABSOLUTE_INCLUDE_DIR})
endif() endif()

View File

@ -38,7 +38,6 @@ else()
PREFIX "${PROJECT_BINARY_DIR}/luajit-prefix" PREFIX "${PROJECT_BINARY_DIR}/luajit-prefix"
GIT_REPOSITORY "https://github.com/moonjit/moonjit" GIT_REPOSITORY "https://github.com/moonjit/moonjit"
GIT_TAG "2.1.2" GIT_TAG "2.1.2"
PATCH_COMMAND sed -i "s/luaL_reg/luaL_Reg/g" ${PROJECT_SOURCE_DIR}/userspace/libsinsp/chisel.cpp && sed -i "s/luaL_reg/luaL_Reg/g" ${PROJECT_SOURCE_DIR}/userspace/libsinsp/lua_parser.cpp && sed -i "s/luaL_getn/lua_objlen /g" ${PROJECT_SOURCE_DIR}/userspace/libsinsp/lua_parser_api.cpp
CONFIGURE_COMMAND "" CONFIGURE_COMMAND ""
BUILD_COMMAND ${CMD_MAKE} BUILD_COMMAND ${CMD_MAKE}
BUILD_IN_SOURCE 1 BUILD_IN_SOURCE 1
@ -49,17 +48,26 @@ else()
PREFIX "${PROJECT_BINARY_DIR}/luajit-prefix" PREFIX "${PROJECT_BINARY_DIR}/luajit-prefix"
GIT_REPOSITORY "https://github.com/linux-on-ibm-z/LuaJIT.git" GIT_REPOSITORY "https://github.com/linux-on-ibm-z/LuaJIT.git"
GIT_TAG "v2.1" GIT_TAG "v2.1"
PATCH_COMMAND sed -i "s/luaL_reg/luaL_Reg/g" ${PROJECT_SOURCE_DIR}/userspace/libsinsp/chisel.cpp && sed -i "s/luaL_reg/luaL_Reg/g" ${PROJECT_SOURCE_DIR}/userspace/libsinsp/lua_parser.cpp && sed -i "s/luaL_getn/lua_objlen /g" ${PROJECT_SOURCE_DIR}/userspace/libsinsp/lua_parser_api.cpp
CONFIGURE_COMMAND "" CONFIGURE_COMMAND ""
BUILD_COMMAND ${CMD_MAKE} BUILD_COMMAND ${CMD_MAKE}
BUILD_IN_SOURCE 1 BUILD_IN_SOURCE 1
BUILD_BYPRODUCTS ${LUAJIT_LIB} BUILD_BYPRODUCTS ${LUAJIT_LIB}
INSTALL_COMMAND "") INSTALL_COMMAND "")
elseif(APPLE)
ExternalProject_Add(luajit
PREFIX "${PROJECT_BINARY_DIR}/luajit-prefix"
URL "https://github.com/LuaJIT/LuaJIT/archive/v2.1.0-beta3.tar.gz"
URL_HASH "SHA256=409f7fe570d3c16558e594421c47bdd130238323c9d6fd6c83dedd2aaeb082a8"
CONFIGURE_COMMAND ""
BUILD_COMMAND make MACOSX_DEPLOYMENT_TARGET=10.14
BUILD_IN_SOURCE 1
BUILD_BYPRODUCTS ${LUAJIT_LIB}
INSTALL_COMMAND "")
else() else()
ExternalProject_Add(luajit ExternalProject_Add(luajit
PREFIX "${PROJECT_BINARY_DIR}/luajit-prefix" PREFIX "${PROJECT_BINARY_DIR}/luajit-prefix"
URL "https://github.com/LuaJIT/LuaJIT/archive/v2.0.3.tar.gz" GIT_REPOSITORY "https://github.com/LuaJIT/LuaJIT"
URL_HASH "SHA256=8da3d984495a11ba1bce9a833ba60e18b532ca0641e7d90d97fafe85ff014baa" GIT_TAG "f3c856915b4ce7ccd24341e8ac73e8a9fd934171"
CONFIGURE_COMMAND "" CONFIGURE_COMMAND ""
BUILD_COMMAND ${CMD_MAKE} BUILD_COMMAND ${CMD_MAKE}
BUILD_IN_SOURCE 1 BUILD_IN_SOURCE 1
@ -69,8 +77,8 @@ else()
else() else()
ExternalProject_Add(luajit ExternalProject_Add(luajit
PREFIX "${PROJECT_BINARY_DIR}/luajit-prefix" PREFIX "${PROJECT_BINARY_DIR}/luajit-prefix"
URL "https://github.com/LuaJIT/LuaJIT/archive/v2.0.3.tar.gz" URL "https://github.com/LuaJIT/LuaJIT/archive/v2.1.0-beta3.tar.gz"
URL_HASH "SHA256=8da3d984495a11ba1bce9a833ba60e18b532ca0641e7d90d97fafe85ff014baa" URL_HASH "SHA256=409f7fe570d3c16558e594421c47bdd130238323c9d6fd6c83dedd2aaeb082a8"
CONFIGURE_COMMAND "" CONFIGURE_COMMAND ""
BUILD_COMMAND msvcbuild.bat BUILD_COMMAND msvcbuild.bat
BUILD_BYPRODUCTS ${LUAJIT_LIB} BUILD_BYPRODUCTS ${LUAJIT_LIB}

View File

@ -14,14 +14,15 @@ else()
set(OPENSSL_INCLUDE_DIR "${PROJECT_BINARY_DIR}/openssl-prefix/src/openssl/include") set(OPENSSL_INCLUDE_DIR "${PROJECT_BINARY_DIR}/openssl-prefix/src/openssl/include")
set(OPENSSL_LIBRARY_SSL "${OPENSSL_INSTALL_DIR}/lib/libssl.a") set(OPENSSL_LIBRARY_SSL "${OPENSSL_INSTALL_DIR}/lib/libssl.a")
set(OPENSSL_LIBRARY_CRYPTO "${OPENSSL_INSTALL_DIR}/lib/libcrypto.a") set(OPENSSL_LIBRARY_CRYPTO "${OPENSSL_INSTALL_DIR}/lib/libcrypto.a")
set(OPENSSL_LIBRARIES ${OPENSSL_LIBRARY_SSL} ${OPENSSL_LIBRARY_CRYPTO})
if(NOT TARGET openssl) if(NOT TARGET openssl)
message(STATUS "Using bundled openssl in '${OPENSSL_BUNDLE_DIR}'") message(STATUS "Using bundled openssl in '${OPENSSL_BUNDLE_DIR}'")
ExternalProject_Add(openssl ExternalProject_Add(openssl
PREFIX "${PROJECT_BINARY_DIR}/openssl-prefix" PREFIX "${PROJECT_BINARY_DIR}/openssl-prefix"
URL "https://github.com/openssl/openssl/archive/OpenSSL_1_0_2u.tar.gz" URL "https://github.com/openssl/openssl/archive/OpenSSL_1_1_1l.tar.gz"
URL_HASH "SHA256=82fa58e3f273c53128c6fe7e3635ec8cda1319a10ce1ad50a987c3df0deeef05" URL_HASH "SHA256=dac036669576e83e8523afdb3971582f8b5d33993a2d6a5af87daa035f529b4f"
CONFIGURE_COMMAND ./config no-shared --prefix=${OPENSSL_INSTALL_DIR} CONFIGURE_COMMAND ./config no-shared --prefix=${OPENSSL_INSTALL_DIR}
BUILD_COMMAND ${CMD_MAKE} BUILD_COMMAND ${CMD_MAKE}
BUILD_IN_SOURCE 1 BUILD_IN_SOURCE 1

View File

@ -24,21 +24,6 @@ else()
if(NOT TARGET protobuf) if(NOT TARGET protobuf)
message(STATUS "Using bundled protobuf in '${PROTOBUF_SRC}'") message(STATUS "Using bundled protobuf in '${PROTOBUF_SRC}'")
if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "s390x")
ExternalProject_Add(protobuf
PREFIX "${PROJECT_BINARY_DIR}/protobuf-prefix"
DEPENDS openssl zlib
URL "http://download.sysdig.com/dependencies/protobuf-cpp-3.5.0.tar.gz"
URL_MD5 "e4ba8284a407712168593e79e6555eb2"
PATCH_COMMAND wget http://download.sysdig.com/dependencies/protobuf-3.5.0-s390x.patch && patch -p1 -i protobuf-3.5.0-s390x.patch
# TODO what if using system zlib?
CONFIGURE_COMMAND /usr/bin/env CPPFLAGS=-I${ZLIB_INCLUDE} LDFLAGS=-L${ZLIB_SRC} ./configure --with-zlib --disable-shared --enable-static --prefix=${PROTOBUF_INSTALL_DIR}
COMMAND aclocal && automake
BUILD_COMMAND ${CMD_MAKE}
BUILD_IN_SOURCE 1
BUILD_BYPRODUCTS ${PROTOC} ${PROTOBUF_INCLUDE} ${PROTOBUF_LIB}
INSTALL_COMMAND make install)
else()
ExternalProject_Add(protobuf ExternalProject_Add(protobuf
PREFIX "${PROJECT_BINARY_DIR}/protobuf-prefix" PREFIX "${PROJECT_BINARY_DIR}/protobuf-prefix"
DEPENDS openssl zlib DEPENDS openssl zlib
@ -52,6 +37,5 @@ else()
INSTALL_COMMAND make install) INSTALL_COMMAND make install)
endif() endif()
endif() endif()
endif()
include_directories("${PROTOBUF_INCLUDE}") include_directories("${PROTOBUF_INCLUDE}")

View File

@ -0,0 +1,34 @@
#
# Copyright (C) 2020 The Falco Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#
#
# Valijson (https://github.com/tristanpenman/valijson/)
#
if(VALIJSON_INCLUDE)
# we already have valijson
else()
set(VALIJSON_SRC "${PROJECT_BINARY_DIR}/valijson-prefix/src/valijson")
set(VALIJSON_INCLUDE "${VALIJSON_SRC}/include")
message(STATUS "Using bundled valijson in '${VALIJSON_SRC}'")
ExternalProject_Add(valijson
PREFIX "${PROJECT_BINARY_DIR}/valijson-prefix"
URL "https://github.com/tristanpenman/valijson/archive/refs/tags/v0.6.tar.gz"
URL_HASH "SHA256=e06bf78fc1d26d4956fabc182408ebbbc47e3a6699778cda4aa439c2a6110b09"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND "")
endif()
include_directories("${VALIJSON_INCLUDE}")

View File

@ -7,6 +7,9 @@
option(BUILD_DRIVER "Build the driver on Linux" ON) option(BUILD_DRIVER "Build the driver on Linux" ON)
option(ENABLE_DKMS "Enable DKMS on Linux" ON) option(ENABLE_DKMS "Enable DKMS on Linux" ON)
if(NOT DEFINED DRIVER_COMPONENT_NAME)
set(DRIVER_COMPONENT_NAME "scap-driver")
endif()
# The driver build process is somewhat involved because we use the same # The driver build process is somewhat involved because we use the same
# sources for building the driver locally and for shipping as a DKMS module. # sources for building the driver locally and for shipping as a DKMS module.
@ -60,6 +63,7 @@ set(DRIVER_SOURCES
ppm_cputime.c ppm_cputime.c
ppm_compat_unistd_32.h ppm_compat_unistd_32.h
ppm_version.h ppm_version.h
systype_compat.h
) )
foreach(FILENAME IN LISTS DRIVER_SOURCES) foreach(FILENAME IN LISTS DRIVER_SOURCES)
@ -102,8 +106,8 @@ if(ENABLE_DKMS)
${CMAKE_CURRENT_BINARY_DIR}/src/dkms.conf ${CMAKE_CURRENT_BINARY_DIR}/src/dkms.conf
${CMAKE_CURRENT_BINARY_DIR}/src/driver_config.h ${CMAKE_CURRENT_BINARY_DIR}/src/driver_config.h
${DRIVER_SOURCES} ${DRIVER_SOURCES}
DESTINATION "src/${PACKAGE_NAME}-${PROBE_VERSION}" DESTINATION "src/${DRIVER_PACKAGE_NAME}-${PROBE_VERSION}"
COMPONENT agent-kmodule) COMPONENT ${DRIVER_COMPONENT_NAME})
endif() endif()

View File

@ -29,5 +29,5 @@ install(FILES
quirks.h quirks.h
ring_helpers.h ring_helpers.h
types.h types.h
DESTINATION "src/${PACKAGE_NAME}-${PROBE_VERSION}/bpf" DESTINATION "src/${DRIVER_PACKAGE_NAME}-${PROBE_VERSION}/bpf"
COMPONENT agent-kmodule) COMPONENT ${DRIVER_COMPONENT_NAME})

View File

@ -18,8 +18,17 @@ static int (*bpf_map_delete_elem)(void *map, void *key) =
(void *)BPF_FUNC_map_delete_elem; (void *)BPF_FUNC_map_delete_elem;
static int (*bpf_probe_read)(void *dst, int size, void *unsafe_ptr) = static int (*bpf_probe_read)(void *dst, int size, void *unsafe_ptr) =
(void *)BPF_FUNC_probe_read; (void *)BPF_FUNC_probe_read;
static unsigned long long (*bpf_ktime_get_ns)(void) =
/* Introduced in linux 5.8, see https://github.com/torvalds/linux/commit/71d19214776e61b33da48f7c1b46e522c7f78221 */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,8,0)
static unsigned long long (*bpf_ktime_get_boot_ns)(void) =
(void *)BPF_FUNC_ktime_get_boot_ns;
#else
/* fallback at using old, non suspend-time aware, helper */
static unsigned long long (*bpf_ktime_get_boot_ns)(void) =
(void *)BPF_FUNC_ktime_get_ns; (void *)BPF_FUNC_ktime_get_ns;
#endif
static int (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) = static int (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) =
(void *)BPF_FUNC_trace_printk; (void *)BPF_FUNC_trace_printk;
static void (*bpf_tail_call)(void *ctx, void *map, int index) = static void (*bpf_tail_call)(void *ctx, void *map, int index) =

View File

@ -406,6 +406,28 @@ static __always_inline u32 bpf_compute_snaplen(struct filler_data *data,
return res; return res;
} }
static __always_inline int unix_socket_path(char *dest, const char *user_ptr, size_t size) {
int res = bpf_probe_read_str(dest,
size,
user_ptr);
/*
* Extract from: https://man7.org/linux/man-pages/man7/unix.7.html
* an abstract socket address is distinguished (from a
* pathname socket) by the fact that sun_path[0] is a null byte
* ('\0'). The socket's address in this namespace is given by
* the additional bytes in sun_path that are covered by the
* specified length of the address structure.
*/
if (res == 1) {
dest[0] = '@';
res = bpf_probe_read_str(dest + 1,
size - 1, // account for '@'
user_ptr + 1);
res++; // account for '@'
}
return res;
}
static __always_inline u16 bpf_pack_addr(struct filler_data *data, static __always_inline u16 bpf_pack_addr(struct filler_data *data,
struct sockaddr *usrsockaddr, struct sockaddr *usrsockaddr,
int ulen) int ulen)
@ -487,9 +509,9 @@ static __always_inline u16 bpf_pack_addr(struct filler_data *data,
data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF] = socket_family_to_scap(family); data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF] = socket_family_to_scap(family);
res = bpf_probe_read_str(&data->buf[(data->state->tail_ctx.curoff + 1) & SCRATCH_SIZE_HALF], res = unix_socket_path(&data->buf[(data->state->tail_ctx.curoff + 1) & SCRATCH_SIZE_HALF],
UNIX_PATH_MAX, usrsockaddr_un->sun_path,
usrsockaddr_un->sun_path); UNIX_PATH_MAX);
size += res; size += res;
@ -697,9 +719,9 @@ static __always_inline long bpf_fd_to_socktuple(struct filler_data *data,
us_name = usrsockaddr_un->sun_path; us_name = usrsockaddr_un->sun_path;
} }
int res = bpf_probe_read_str(&data->buf[(data->state->tail_ctx.curoff + 1 + 8 + 8) & SCRATCH_SIZE_HALF], int res = unix_socket_path(&data->buf[(data->state->tail_ctx.curoff + 1 + 8 + 8) & SCRATCH_SIZE_HALF],
UNIX_PATH_MAX, us_name,
us_name); UNIX_PATH_MAX);
size += res; size += res;
@ -791,6 +813,10 @@ static __always_inline int __bpf_val_to_ring(struct filler_data *data,
if (!data->curarg_already_on_frame) { if (!data->curarg_already_on_frame) {
volatile u16 read_size = len; volatile u16 read_size = len;
curoff_bounded = data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF;
if (data->state->tail_ctx.curoff > SCRATCH_SIZE_HALF)
return PPM_FAILURE_BUFFER_FULL;
#ifdef BPF_FORBIDS_ZERO_ACCESS #ifdef BPF_FORBIDS_ZERO_ACCESS
if (read_size) if (read_size)
if (bpf_probe_read(&data->buf[curoff_bounded], if (bpf_probe_read(&data->buf[curoff_bounded],
@ -933,9 +959,7 @@ static __always_inline int bpf_val_to_ring_type(struct filler_data *data,
static __always_inline bool bpf_in_ia32_syscall() static __always_inline bool bpf_in_ia32_syscall()
{ {
#ifdef __ppc64__ #if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86))
return 0;
#else
struct task_struct *task; struct task_struct *task;
u32 status; u32 status;
@ -952,7 +976,9 @@ static __always_inline bool bpf_in_ia32_syscall()
#endif #endif
return status & TS_COMPAT; return status & TS_COMPAT;
#endif // #ifdef __ppc64__ #else /* X86 */
return 0;
#endif /* X86 */
} }
#endif #endif

View File

@ -18,7 +18,7 @@ or GPL2.txt for full copies of the license.
* probe to build. * probe to build.
*/ */
//#define COS_73_WORKAROUND //#define COS_73_WORKAROUND
#include "../systype_compat.h"
#include "../ppm_flag_helpers.h" #include "../ppm_flag_helpers.h"
#include "../ppm_version.h" #include "../ppm_version.h"
@ -26,6 +26,14 @@ or GPL2.txt for full copies of the license.
#include <linux/audit.h> #include <linux/audit.h>
/* Linux kernel 4.15 introduced the new const `UID_GID_MAP_MAX_BASE_EXTENTS` in place of
* the old `UID_GID_MAP_MAX_EXTENTS`, which instead has changed its meaning.
* For more info see https://github.com/torvalds/linux/commit/6397fac4915ab3002dc15aae751455da1a852f25
*/
#ifndef UID_GID_MAP_MAX_BASE_EXTENTS
#define UID_GID_MAP_MAX_BASE_EXTENTS 5
#endif
/* /*
* Linux 5.6 kernels no longer include the old 32-bit timeval * Linux 5.6 kernels no longer include the old 32-bit timeval
* structures. But the syscalls (might) still use them. * structures. But the syscalls (might) still use them.
@ -100,19 +108,25 @@ FILLER_RAW(terminate_filler)
bpf_printk("PPM_FAILURE_BUFFER_FULL event=%d curarg=%d\n", bpf_printk("PPM_FAILURE_BUFFER_FULL event=%d curarg=%d\n",
state->tail_ctx.evt_type, state->tail_ctx.evt_type,
state->tail_ctx.curarg); state->tail_ctx.curarg);
if (state->n_drops_buffer != ULLONG_MAX) {
++state->n_drops_buffer; ++state->n_drops_buffer;
}
break; break;
case PPM_FAILURE_INVALID_USER_MEMORY: case PPM_FAILURE_INVALID_USER_MEMORY:
bpf_printk("PPM_FAILURE_INVALID_USER_MEMORY event=%d curarg=%d\n", bpf_printk("PPM_FAILURE_INVALID_USER_MEMORY event=%d curarg=%d\n",
state->tail_ctx.evt_type, state->tail_ctx.evt_type,
state->tail_ctx.curarg); state->tail_ctx.curarg);
if (state->n_drops_pf != ULLONG_MAX) {
++state->n_drops_pf; ++state->n_drops_pf;
}
break; break;
case PPM_FAILURE_BUG: case PPM_FAILURE_BUG:
bpf_printk("PPM_FAILURE_BUG event=%d curarg=%d\n", bpf_printk("PPM_FAILURE_BUG event=%d curarg=%d\n",
state->tail_ctx.evt_type, state->tail_ctx.evt_type,
state->tail_ctx.curarg); state->tail_ctx.curarg);
if (state->n_drops_bug != ULLONG_MAX) {
++state->n_drops_bug; ++state->n_drops_bug;
}
break; break;
case PPM_SKIP_EVENT: case PPM_SKIP_EVENT:
break; break;
@ -435,7 +449,6 @@ static __always_inline int bpf_parse_readv_writev_bufs(struct filler_data *data,
if (flags & PRB_FLAG_PUSH_DATA) { if (flags & PRB_FLAG_PUSH_DATA) {
if (size > 0) { if (size > 0) {
unsigned long off = _READ(data->state->tail_ctx.curoff); unsigned long off = _READ(data->state->tail_ctx.curoff);
unsigned long off_bounded;
unsigned long remaining = size; unsigned long remaining = size;
int j; int j;
@ -446,7 +459,7 @@ static __always_inline int bpf_parse_readv_writev_bufs(struct filler_data *data,
if (j == iovcnt) if (j == iovcnt)
break; break;
off_bounded = off & SCRATCH_SIZE_HALF; unsigned long off_bounded = off & SCRATCH_SIZE_HALF;
if (off > SCRATCH_SIZE_HALF) if (off > SCRATCH_SIZE_HALF)
break; break;
@ -747,6 +760,52 @@ FILLER(sys_mmap_e, true)
return res; return res;
} }
FILLER(sys_mprotect_e, true)
{
unsigned long val;
int res;
/*
* addr
*/
val = bpf_syscall_get_argument(data, 0);
res = bpf_val_to_ring(data, val);
if (res != PPM_SUCCESS)
return res;
/*
* length
*/
val = bpf_syscall_get_argument(data, 1);
res = bpf_val_to_ring(data, val);
if (res != PPM_SUCCESS)
return res;
/*
* prot
*/
val = bpf_syscall_get_argument(data, 2);
res = bpf_val_to_ring(data, prot_flags_to_scap(val));
if (res != PPM_SUCCESS)
return res;
return res;
}
FILLER(sys_mprotect_x, true)
{
long retval;
int res;
/*
* res
*/
retval = bpf_syscall_get_retval(data->ctx);
res = bpf_val_to_ring(data, retval);
return res;
}
FILLER(sys_fcntl_e, true) FILLER(sys_fcntl_e, true)
{ {
unsigned long val; unsigned long val;
@ -1361,6 +1420,59 @@ FILLER(sys_execve_e, true)
return res; return res;
} }
FILLER(sys_execveat_e, true)
{
unsigned long val;
unsigned long flags;
int res;
/*
* dirfd
*/
val = bpf_syscall_get_argument(data, 0);
if ((int)val == AT_FDCWD)
{
val = PPM_AT_FDCWD;
}
res = bpf_val_to_ring(data, val);
if (res != PPM_SUCCESS)
{
return res;
}
/*
* pathname
*/
val = bpf_syscall_get_argument(data, 1);
res = bpf_val_to_ring(data, val);
if (res == PPM_FAILURE_INVALID_USER_MEMORY)
{
char na[] = "<NA>";
res = bpf_val_to_ring(data, (unsigned long)na);
}
if (res != PPM_SUCCESS)
{
return res;
}
/*
* flags
*/
val = bpf_syscall_get_argument(data, 4);
flags = execveat_flags_to_scap(val);
res = bpf_val_to_ring(data, flags);
if (res != PPM_SUCCESS)
{
return res;
}
return res;
}
static __always_inline int bpf_ppm_get_tty(struct task_struct *task) static __always_inline int bpf_ppm_get_tty(struct task_struct *task)
{ {
struct signal_struct *sig; struct signal_struct *sig;
@ -1395,7 +1507,7 @@ static __always_inline int bpf_ppm_get_tty(struct task_struct *task)
static __always_inline struct pid *bpf_task_pid(struct task_struct *task) static __always_inline struct pid *bpf_task_pid(struct task_struct *task)
{ {
#if (PPM_RHEL_RELEASE_CODE > 0 && PPM_RHEL_RELEASE_CODE >= PPM_RHEL_RELEASE_VERSION(8, 0)) #if (PPM_RHEL_RELEASE_CODE > 0 && PPM_RHEL_RELEASE_CODE >= PPM_RHEL_RELEASE_VERSION(8, 1))
return _READ(task->thread_pid); return _READ(task->thread_pid);
#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0) #elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
return _READ(task->pids[PIDTYPE_PID].pid); return _READ(task->pids[PIDTYPE_PID].pid);
@ -1434,7 +1546,7 @@ static __always_inline pid_t bpf_pid_nr_ns(struct pid *pid,
return nr; return nr;
} }
#if ((PPM_RHEL_RELEASE_CODE > 0 && PPM_RHEL_RELEASE_CODE >= PPM_RHEL_RELEASE_VERSION(8, 0))) || LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) #if ((PPM_RHEL_RELEASE_CODE > 0 && PPM_RHEL_RELEASE_CODE >= PPM_RHEL_RELEASE_VERSION(8, 1))) || LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
static __always_inline struct pid **bpf_task_pid_ptr(struct task_struct *task, static __always_inline struct pid **bpf_task_pid_ptr(struct task_struct *task,
enum pid_type type) enum pid_type type)
{ {
@ -1453,7 +1565,7 @@ static __always_inline pid_t bpf_task_pid_nr_ns(struct task_struct *task,
if (!ns) if (!ns)
ns = bpf_task_active_pid_ns(task); ns = bpf_task_active_pid_ns(task);
#if (PPM_RHEL_RELEASE_CODE > 0 && PPM_RHEL_RELEASE_CODE >= PPM_RHEL_RELEASE_VERSION(8, 0)) #if (PPM_RHEL_RELEASE_CODE > 0 && PPM_RHEL_RELEASE_CODE >= PPM_RHEL_RELEASE_VERSION(8, 1))
nr = bpf_pid_nr_ns(_READ(*bpf_task_pid_ptr(task, type)), ns); nr = bpf_pid_nr_ns(_READ(*bpf_task_pid_ptr(task, type)), ns);
#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0) #elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
if (type != PIDTYPE_PID) { if (type != PIDTYPE_PID) {
@ -1478,7 +1590,7 @@ static __always_inline pid_t bpf_task_pid_vnr(struct task_struct *task)
static __always_inline pid_t bpf_task_tgid_vnr(struct task_struct *task) static __always_inline pid_t bpf_task_tgid_vnr(struct task_struct *task)
{ {
#if (PPM_RHEL_RELEASE_CODE > 0 && PPM_RHEL_RELEASE_CODE >= PPM_RHEL_RELEASE_VERSION(8, 0)) #if (PPM_RHEL_RELEASE_CODE > 0 && PPM_RHEL_RELEASE_CODE >= PPM_RHEL_RELEASE_VERSION(8, 1))
return bpf_task_pid_nr_ns(task, PIDTYPE_TGID, NULL); return bpf_task_pid_nr_ns(task, PIDTYPE_TGID, NULL);
#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0) #elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
return bpf_task_pid_nr_ns(task, __PIDTYPE_TGID, NULL); return bpf_task_pid_nr_ns(task, __PIDTYPE_TGID, NULL);
@ -1662,6 +1774,212 @@ static __always_inline int bpf_accumulate_argv_or_env(struct filler_data *data,
return PPM_SUCCESS; return PPM_SUCCESS;
} }
// log(NGROUPS_MAX) = log(65536)
#define MAX_GROUP_SEARCH_DEPTH 16
static __always_inline bool bpf_groups_search(struct group_info *group_info, kgid_t grp) {
unsigned int left, right;
if (!group_info) {
return 0;
}
left = 0;
right = _READ(group_info->ngroups);
#pragma unroll MAX_GROUP_SEARCH_DEPTH
for (int j = 0; j < MAX_GROUP_SEARCH_DEPTH; j++) {
if (left >= right) {
break;
}
unsigned int mid = (left+right)/2;
if (gid_gt(grp, _READ(group_info->gid[mid]))) {
left = mid + 1;
} else if (gid_lt(grp, _READ(group_info->gid[mid]))) {
right = mid;
} else {
return true;
}
}
return false;
}
// log(UID_GID_MAP_MAX_EXTENTS) = log(340)
#define MAX_EXTENT_SEARCH_DEPTH 9
static __always_inline struct uid_gid_extent *
bpf_map_id_up_max(unsigned extents, struct uid_gid_map *map, u32 id)
{
u32 left, right;
left = 0;
right = _READ(map->nr_extents);
#pragma unroll MAX_EXTENT_SEARCH_DEPTH
for (int j = 0; j < MAX_EXTENT_SEARCH_DEPTH; j++) {
if (left >= right) {
break;
}
unsigned int mid = (left+right)/2;
u32 mid_id = _READ(map->extent[mid].lower_first);
if (id > mid_id) {
left = mid + 1;
} else if (id < mid_id) {
right = mid;
} else {
return &map->extent[mid];
}
}
return NULL;
}
static __always_inline struct uid_gid_extent *
bpf_map_id_up_base(unsigned extents, struct uid_gid_map *map, u32 id)
{
unsigned idx;
u32 first, last;
#pragma unroll UID_GID_MAP_MAX_BASE_EXTENTS
for (idx = 0; idx < UID_GID_MAP_MAX_BASE_EXTENTS; idx++) {
if (idx < extents) {
first = _READ(map->extent[idx].lower_first);
last = first + _READ(map->extent[idx].count) - 1;
if (id >= first && id <= last)
return &map->extent[idx];
}
}
return NULL;
}
// UP means get NS id (uid/gid) from kuid/kgid
static __always_inline u32 bpf_map_id_up(struct uid_gid_map *map, u32 id)
{
struct uid_gid_extent *extent;
unsigned extents = _READ(map->nr_extents);
if (extents <= UID_GID_MAP_MAX_BASE_EXTENTS) {
extent = bpf_map_id_up_base(extents, map, id);
}
/* Kernel 4.15 increased the number of extents to `340` while all the previous kernels have
* the limit set to `5`. So the `if` case should be enough.
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0))
else {
extent = bpf_map_id_up_max(extents, map, id);
}
#endif
/* Map the id or note failure */
if (extent) {
id = (id - _READ(extent->lower_first)) + _READ(extent->first);
} else {
id = (u32) - 1;
}
return id;
}
static __always_inline bool bpf_kuid_has_mapping(struct user_namespace *targ, kuid_t kuid)
{
/* Map the uid from a global kernel uid */
return bpf_map_id_up(&targ->uid_map, __kuid_val(kuid)) != (uid_t) -1;
}
static __always_inline bool bpf_kgid_has_mapping(struct user_namespace *targ, kgid_t kgid)
{
return bpf_map_id_up(&targ->gid_map, __kgid_val(kgid)) != (gid_t) -1;
}
static __always_inline bool get_exe_writable(struct task_struct *task)
{
struct file *exe_file;
struct mm_struct *mm;
mm = _READ(task->mm);
exe_file = _READ(mm->exe_file);
if (!exe_file) {
return false;
}
struct inode *inode = _READ(exe_file->f_inode);
umode_t i_mode = _READ(inode->i_mode);
unsigned i_flags = _READ(inode->i_flags);
struct super_block *sb = _READ(inode->i_sb);
kuid_t i_uid = _READ(inode->i_uid);
kgid_t i_gid = _READ(inode->i_gid);
struct cred *cred = (struct cred*) _READ(task->cred);
kuid_t fsuid = _READ(cred->fsuid);
kgid_t fsgid = _READ(cred->fsgid);
struct group_info *group_info = _READ(cred->group_info);
// basic inode_permission()
// check superblock permissions, i.e. if the FS is read only
if ((_READ(sb->s_flags) & SB_RDONLY) && (S_ISREG(i_mode) || S_ISDIR(i_mode) || S_ISLNK(i_mode))) {
return false;
}
if (i_flags & S_IMMUTABLE) {
return false;
}
// HAS_UNMAPPED_ID()
if (!uid_valid(i_uid) || !gid_valid(i_gid)) {
return false;
}
// inode_owner_or_capable check. If the owner matches the exe counts as writable
if (uid_eq(fsuid, i_uid)) {
return true;
}
// Basic file permission check -- this may not work in all cases as kernel functions are more complex
// and take into account different types of ACLs which can use custom function pointers,
// but I don't think we can inspect those in eBPF
// basic acl_permission_check()
// XXX this doesn't attempt to locate extra POSIX ACL checks (if supported by the kernel)
umode_t mode = i_mode;
if (uid_eq(i_uid, fsuid)) {
mode >>= 6;
} else {
bool in_group = false;
if (gid_eq(i_gid, fsgid)) {
in_group = true;
} else {
in_group = bpf_groups_search(group_info, i_gid);
}
if (in_group) {
mode >>= 3;
}
}
if ((MAY_WRITE & ~mode) == 0) {
return true;
}
struct user_namespace *ns = _READ(cred->user_ns);
bool kuid_mapped = bpf_kuid_has_mapping(ns, i_uid);
bool kgid_mapped = bpf_kgid_has_mapping(ns, i_gid);
if (cap_raised(_READ(cred->cap_effective), CAP_DAC_OVERRIDE) && kuid_mapped && kgid_mapped) {
return true;
}
// Check if the user is capable. Even if it doesn't own the file or the read bits are not set, root with CAP_FOWNER can do what it wants.
if (cap_raised(_READ(cred->cap_effective), CAP_FOWNER) && kuid_mapped) {
return true;
}
return false;
}
FILLER(proc_startupdate, true) FILLER(proc_startupdate, true)
{ {
struct task_struct *real_parent; struct task_struct *real_parent;
@ -1727,11 +2045,25 @@ FILLER(proc_startupdate, true)
else else
data->buf[(data->state->tail_ctx.curoff + args_len - 1) & SCRATCH_SIZE_MAX] = 0; data->buf[(data->state->tail_ctx.curoff + args_len - 1) & SCRATCH_SIZE_MAX] = 0;
} }
} else if (data->state->tail_ctx.evt_type == PPME_SYSCALL_EXECVE_19_X) { } else if (data->state->tail_ctx.evt_type == PPME_SYSCALL_EXECVE_19_X ||
data->state->tail_ctx.evt_type == PPME_SYSCALL_EXECVEAT_X ) {
unsigned long val; unsigned long val;
char **argv; char **argv;
switch (data->state->tail_ctx.evt_type)
{
case PPME_SYSCALL_EXECVE_19_X:
val = bpf_syscall_get_argument(data, 1); val = bpf_syscall_get_argument(data, 1);
break;
case PPME_SYSCALL_EXECVEAT_X:
val = bpf_syscall_get_argument(data, 2);
break;
default:
val = 0;
break;
}
argv = (char **)val; argv = (char **)val;
res = bpf_accumulate_argv_or_env(data, argv, &args_len); res = bpf_accumulate_argv_or_env(data, argv, &args_len);
@ -2009,13 +2341,15 @@ FILLER(proc_startupdate_3, true)
vpid = bpf_task_tgid_vnr(task); vpid = bpf_task_tgid_vnr(task);
res = bpf_val_to_ring_type(data, vpid, PT_PID); res = bpf_val_to_ring_type(data, vpid, PT_PID);
} else if (data->state->tail_ctx.evt_type == PPME_SYSCALL_EXECVE_19_X) { } else if (data->state->tail_ctx.evt_type == PPME_SYSCALL_EXECVE_19_X ||
data->state->tail_ctx.evt_type == PPME_SYSCALL_EXECVEAT_X) {
/* /*
* execve-only parameters * execve-only parameters
*/ */
long env_len = 0; long env_len = 0;
kuid_t loginuid; kuid_t loginuid;
int tty; int tty;
struct file *exe_file;
/* /*
* environ * environ
@ -2050,7 +2384,21 @@ FILLER(proc_startupdate_3, true)
unsigned long val; unsigned long val;
char **envp; char **envp;
switch (data->state->tail_ctx.evt_type)
{
case PPME_SYSCALL_EXECVE_19_X:
val = bpf_syscall_get_argument(data, 2); val = bpf_syscall_get_argument(data, 2);
break;
case PPME_SYSCALL_EXECVEAT_X:
val = bpf_syscall_get_argument(data, 3);
break;
default:
val = 0;
break;
}
envp = (char **)val; envp = (char **)val;
res = bpf_accumulate_argv_or_env(data, envp, &env_len); res = bpf_accumulate_argv_or_env(data, envp, &env_len);
@ -2099,6 +2447,43 @@ FILLER(proc_startupdate_3, true)
res = bpf_val_to_ring_type(data, loginuid.val, PT_INT32); res = bpf_val_to_ring_type(data, loginuid.val, PT_INT32);
if (res != PPM_SUCCESS) if (res != PPM_SUCCESS)
return res; return res;
bpf_tail_call(data->ctx, &tail_map, PPM_FILLER_execve_family_flags);
bpf_printk("Can't tail call execve_family_flags filler\n");
return PPM_FAILURE_BUG;
}
return res;
}
/* This filler avoids a bpf stack overflow on old kernels (like 4.14). */
FILLER(execve_family_flags, true)
{
struct task_struct *task = NULL;
uint32_t flags = 0;
int res = 0;
bool exe_writable = false;
task = (struct task_struct *)bpf_get_current_task();
/*
* exe_writable
*/
exe_writable = get_exe_writable(task);
if (exe_writable)
{
flags |= PPM_EXE_WRITABLE;
}
// write all additional flags for execve family here...
/*
* flags
*/
res = bpf_val_to_ring_type(data, flags, PT_UINT32);
if (res != PPM_SUCCESS)
{
return res;
} }
return res; return res;
@ -2307,6 +2692,83 @@ FILLER(sys_openat_x, true)
return res; return res;
} }
FILLER(sys_openat2_x, true)
{
unsigned long resolve;
unsigned long flags;
unsigned long val;
unsigned long mode;
long retval;
int res;
#ifdef __NR_openat2
struct open_how how;
#endif
retval = bpf_syscall_get_retval(data->ctx);
res = bpf_val_to_ring(data, retval);
if (res != PPM_SUCCESS)
return res;
/*
* dirfd
*/
val = bpf_syscall_get_argument(data, 0);
if ((int)val == AT_FDCWD)
val = PPM_AT_FDCWD;
res = bpf_val_to_ring(data, val);
if (res != PPM_SUCCESS)
return res;
/*
* name
*/
val = bpf_syscall_get_argument(data, 1);
res = bpf_val_to_ring(data, val);
if (res != PPM_SUCCESS)
return res;
#ifdef __NR_openat2
/*
* how: we get the data structure, and put its fields in the buffer one by one
*/
val = bpf_syscall_get_argument(data, 2);
if (bpf_probe_read(&how, sizeof(struct open_how), (void *)val)) {
return PPM_FAILURE_INVALID_USER_MEMORY;
}
flags = open_flags_to_scap(how.flags);
mode = open_modes_to_scap(how.flags, how.mode);
resolve = openat2_resolve_to_scap(how.resolve);
#else
flags = 0;
mode = 0;
resolve = 0;
#endif
/*
* flags (extracted from open_how structure)
* Note that we convert them into the ppm portable representation before pushing them to the ring
*/
res = bpf_val_to_ring(data, flags);
if (res != PPM_SUCCESS)
return res;
/*
* mode (extracted from open_how structure)
* Note that we convert them into the ppm portable representation before pushing them to the ring
*/
res = bpf_val_to_ring(data, mode);
if (res != PPM_SUCCESS)
return res;
/*
* resolve (extracted from open_how structure)
* Note that we convert them into the ppm portable representation before pushing them to the ring
*/
res = bpf_val_to_ring(data, resolve);
return res;
}
FILLER(sys_sendfile_e, true) FILLER(sys_sendfile_e, true)
{ {
unsigned long val; unsigned long val;
@ -3493,10 +3955,31 @@ FILLER(sys_procexit_e, false)
exit_code = _READ(task->exit_code); exit_code = _READ(task->exit_code);
/* Exit status */
res = bpf_val_to_ring(data, exit_code); res = bpf_val_to_ring(data, exit_code);
if (res != PPM_SUCCESS) if (res != PPM_SUCCESS)
return res; return res;
/* Ret code */
res = bpf_val_to_ring(data, __WEXITSTATUS(exit_code));
if (res != PPM_SUCCESS)
return res;
/* If signaled -> signum, else 0 */
if (__WIFSIGNALED(exit_code))
{
res = bpf_val_to_ring(data, __WTERMSIG(exit_code));
} else {
res = bpf_val_to_ring(data, 0);
}
if (res != PPM_SUCCESS)
return res;
/* Did it produce a core? */
res = bpf_val_to_ring(data, __WCOREDUMP(exit_code) != 0);
if (res != PPM_SUCCESS)
return res;
#ifndef BPF_SUPPORTS_RAW_TRACEPOINTS #ifndef BPF_SUPPORTS_RAW_TRACEPOINTS
delete_args(); delete_args();
#endif #endif

View File

@ -460,7 +460,7 @@ static __always_inline void call_filler(void *ctx,
drop_flags = UF_NEVER_DROP; drop_flags = UF_NEVER_DROP;
} }
ts = settings->boot_time + bpf_ktime_get_ns(); ts = settings->boot_time + bpf_ktime_get_boot_ns();
reset_tail_ctx(state, evt_type, ts); reset_tail_ctx(state, evt_type, ts);
/* drop_event can change state->tail_ctx.evt_type */ /* drop_event can change state->tail_ctx.evt_type */

View File

@ -59,6 +59,9 @@ BPF_PROBE("raw_syscalls/", sys_enter, sys_enter_args)
if (!sc_evt) if (!sc_evt)
return 0; return 0;
if (sc_evt->flags & UF_UNINTERESTING)
return 0;
if (sc_evt->flags & UF_USED) { if (sc_evt->flags & UF_USED) {
evt_type = sc_evt->enter_event_type; evt_type = sc_evt->enter_event_type;
drop_flags = sc_evt->flags; drop_flags = sc_evt->flags;
@ -108,6 +111,9 @@ BPF_PROBE("raw_syscalls/", sys_exit, sys_exit_args)
if (!sc_evt) if (!sc_evt)
return 0; return 0;
if (sc_evt->flags & UF_UNINTERESTING)
return 0;
if (sc_evt->flags & UF_USED) { if (sc_evt->flags & UF_USED) {
evt_type = sc_evt->exit_event_type; evt_type = sc_evt->exit_event_type;
drop_flags = sc_evt->flags; drop_flags = sc_evt->flags;

View File

@ -24,9 +24,12 @@ or GPL2.txt for full copies of the license.
#define BPF_FORBIDS_ZERO_ACCESS #define BPF_FORBIDS_ZERO_ACCESS
#endif #endif
/* RAW_TRACEPOINTS logic is x86-specific */
#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86))
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
#define BPF_SUPPORTS_RAW_TRACEPOINTS #define BPF_SUPPORTS_RAW_TRACEPOINTS
#endif #endif
#endif
/* Redefine asm_volatile_goto to work around clang not supporting it /* Redefine asm_volatile_goto to work around clang not supporting it
*/ */

View File

@ -1,4 +1,4 @@
PACKAGE_NAME="@PACKAGE_NAME@" PACKAGE_NAME="@DRIVER_PACKAGE_NAME@"
PACKAGE_VERSION="@PROBE_VERSION@" PACKAGE_VERSION="@PROBE_VERSION@"
BUILT_MODULE_NAME[0]="@PROBE_NAME@" BUILT_MODULE_NAME[0]="@PROBE_NAME@"
DEST_MODULE_LOCATION[0]="/kernel/extra" DEST_MODULE_LOCATION[0]="/kernel/extra"

View File

@ -198,7 +198,7 @@ const struct ppm_event_info g_event_info[PPM_EVENT_MAX] = {
/* PPME_SYSCALL_FORK_X */{"fork", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 16, {{"res", PT_PID, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_INT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"flags", PT_FLAGS32, PF_HEX, clone_flags}, {"uid", PT_UINT32, PF_DEC}, {"gid", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_FORK_X */{"fork", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 16, {{"res", PT_PID, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_INT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"flags", PT_FLAGS32, PF_HEX, clone_flags}, {"uid", PT_UINT32, PF_DEC}, {"gid", PT_UINT32, PF_DEC} } },
/* PPME_SYSCALL_VFORK_E */{"vfork", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 0}, /* PPME_SYSCALL_VFORK_E */{"vfork", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 0},
/* PPME_SYSCALL_VFORK_X */{"vfork", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 16, {{"res", PT_PID, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_INT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"flags", PT_FLAGS32, PF_HEX, clone_flags}, {"uid", PT_UINT32, PF_DEC}, {"gid", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_VFORK_X */{"vfork", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 16, {{"res", PT_PID, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_INT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"flags", PT_FLAGS32, PF_HEX, clone_flags}, {"uid", PT_UINT32, PF_DEC}, {"gid", PT_UINT32, PF_DEC} } },
/* PPME_PROCEXIT_1_E */{"procexit", EC_PROCESS, EF_MODIFIES_STATE, 1, {{"status", PT_ERRNO, PF_DEC} } }, /* PPME_PROCEXIT_1_E */{"procexit", EC_PROCESS, EF_MODIFIES_STATE, 4, {{"status", PT_ERRNO, PF_DEC}, {"ret", PT_ERRNO, PF_DEC}, {"sig", PT_SIGTYPE, PF_DEC}, {"core", PT_UINT8, PF_DEC} } },
/* PPME_NA1 */{"NA1", EC_PROCESS, EF_UNUSED, 0}, /* PPME_NA1 */{"NA1", EC_PROCESS, EF_UNUSED, 0},
/* PPME_SYSCALL_SENDFILE_E */{"sendfile", EC_IO_WRITE, EF_USES_FD | EF_DROP_SIMPLE_CONS, 4, {{"out_fd", PT_FD, PF_DEC}, {"in_fd", PT_FD, PF_DEC}, {"offset", PT_UINT64, PF_DEC}, {"size", PT_UINT64, PF_DEC} } }, /* PPME_SYSCALL_SENDFILE_E */{"sendfile", EC_IO_WRITE, EF_USES_FD | EF_DROP_SIMPLE_CONS, 4, {{"out_fd", PT_FD, PF_DEC}, {"in_fd", PT_FD, PF_DEC}, {"offset", PT_UINT64, PF_DEC}, {"size", PT_UINT64, PF_DEC} } },
/* PPME_SYSCALL_SENDFILE_X */{"sendfile", EC_IO_WRITE, EF_USES_FD | EF_DROP_SIMPLE_CONS, 2, {{"res", PT_ERRNO, PF_DEC}, {"offset", PT_UINT64, PF_DEC} } }, /* PPME_SYSCALL_SENDFILE_X */{"sendfile", EC_IO_WRITE, EF_USES_FD | EF_DROP_SIMPLE_CONS, 2, {{"res", PT_ERRNO, PF_DEC}, {"offset", PT_UINT64, PF_DEC} } },
@ -240,8 +240,8 @@ const struct ppm_event_info g_event_info[PPM_EVENT_MAX] = {
/* PPME_SYSCALL_FORK_20_X */{"fork", EC_PROCESS, EF_MODIFIES_STATE, 20, {{"res", PT_PID, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_INT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"comm", PT_CHARBUF, PF_NA}, {"cgroups", PT_BYTEBUF, PF_NA}, {"flags", PT_FLAGS32, PF_HEX, clone_flags}, {"uid", PT_UINT32, PF_DEC}, {"gid", PT_UINT32, PF_DEC}, {"vtid", PT_PID, PF_DEC}, {"vpid", PT_PID, PF_DEC} } }, /* PPME_SYSCALL_FORK_20_X */{"fork", EC_PROCESS, EF_MODIFIES_STATE, 20, {{"res", PT_PID, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_INT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"comm", PT_CHARBUF, PF_NA}, {"cgroups", PT_BYTEBUF, PF_NA}, {"flags", PT_FLAGS32, PF_HEX, clone_flags}, {"uid", PT_UINT32, PF_DEC}, {"gid", PT_UINT32, PF_DEC}, {"vtid", PT_PID, PF_DEC}, {"vpid", PT_PID, PF_DEC} } },
/* PPME_SYSCALL_VFORK_20_E */{"vfork", EC_PROCESS, EF_MODIFIES_STATE, 0}, /* PPME_SYSCALL_VFORK_20_E */{"vfork", EC_PROCESS, EF_MODIFIES_STATE, 0},
/* PPME_SYSCALL_VFORK_20_X */{"vfork", EC_PROCESS, EF_MODIFIES_STATE, 20, {{"res", PT_PID, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_INT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"comm", PT_CHARBUF, PF_NA}, {"cgroups", PT_BYTEBUF, PF_NA}, {"flags", PT_FLAGS32, PF_HEX, clone_flags}, {"uid", PT_UINT32, PF_DEC}, {"gid", PT_UINT32, PF_DEC}, {"vtid", PT_PID, PF_DEC}, {"vpid", PT_PID, PF_DEC} } }, /* PPME_SYSCALL_VFORK_20_X */{"vfork", EC_PROCESS, EF_MODIFIES_STATE, 20, {{"res", PT_PID, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_INT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"comm", PT_CHARBUF, PF_NA}, {"cgroups", PT_BYTEBUF, PF_NA}, {"flags", PT_FLAGS32, PF_HEX, clone_flags}, {"uid", PT_UINT32, PF_DEC}, {"gid", PT_UINT32, PF_DEC}, {"vtid", PT_PID, PF_DEC}, {"vpid", PT_PID, PF_DEC} } },
/* PPME_CONTAINER_E */{"container", EC_INTERNAL, EF_SKIPPARSERESET | EF_MODIFIES_STATE, 4, {{"id", PT_CHARBUF, PF_NA}, {"type", PT_UINT32, PF_DEC}, {"name", PT_CHARBUF, PF_NA}, {"image", PT_CHARBUF, PF_NA} } }, /* PPME_CONTAINER_E */{"container", EC_INTERNAL, EF_SKIPPARSERESET | EF_MODIFIES_STATE | EF_OLD_VERSION, 4, {{"id", PT_CHARBUF, PF_NA}, {"type", PT_UINT32, PF_DEC}, {"name", PT_CHARBUF, PF_NA}, {"image", PT_CHARBUF, PF_NA} } },
/* PPME_CONTAINER_X */{"container", EC_INTERNAL, EF_UNUSED, 0}, /* PPME_CONTAINER_X */{"container", EC_INTERNAL, EF_UNUSED | EF_OLD_VERSION, 0},
/* PPME_SYSCALL_EXECVE_16_E */{"execve", EC_PROCESS, EF_MODIFIES_STATE, 0}, /* PPME_SYSCALL_EXECVE_16_E */{"execve", EC_PROCESS, EF_MODIFIES_STATE, 0},
/* PPME_SYSCALL_EXECVE_16_X */{"execve", EC_PROCESS, EF_MODIFIES_STATE, 16, {{"res", PT_ERRNO, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_UINT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"comm", PT_CHARBUF, PF_NA}, {"cgroups", PT_BYTEBUF, PF_NA}, {"env", PT_BYTEBUF, PF_NA} } }, /* PPME_SYSCALL_EXECVE_16_X */{"execve", EC_PROCESS, EF_MODIFIES_STATE, 16, {{"res", PT_ERRNO, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_UINT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"comm", PT_CHARBUF, PF_NA}, {"cgroups", PT_BYTEBUF, PF_NA}, {"env", PT_BYTEBUF, PF_NA} } },
/* PPME_SIGNALDELIVER_E */ {"signaldeliver", EC_SIGNAL, EF_DROP_SIMPLE_CONS, 3, {{"spid", PT_PID, PF_DEC}, {"dpid", PT_PID, PF_DEC}, {"sig", PT_SIGTYPE, PF_DEC} } }, /* PPME_SIGNALDELIVER_E */ {"signaldeliver", EC_SIGNAL, EF_DROP_SIMPLE_CONS, 3, {{"spid", PT_PID, PF_DEC}, {"dpid", PT_PID, PF_DEC}, {"sig", PT_SIGTYPE, PF_DEC} } },
@ -305,7 +305,7 @@ const struct ppm_event_info g_event_info[PPM_EVENT_MAX] = {
/* PPME_PAGE_FAULT_E */ {"page_fault", EC_OTHER, EF_SKIPPARSERESET | EF_DROP_SIMPLE_CONS, 3, {{"addr", PT_UINT64, PF_HEX}, {"ip", PT_UINT64, PF_HEX}, {"error", PT_FLAGS32, PF_HEX, pf_flags} } }, /* PPME_PAGE_FAULT_E */ {"page_fault", EC_OTHER, EF_SKIPPARSERESET | EF_DROP_SIMPLE_CONS, 3, {{"addr", PT_UINT64, PF_HEX}, {"ip", PT_UINT64, PF_HEX}, {"error", PT_FLAGS32, PF_HEX, pf_flags} } },
/* PPME_PAGE_FAULT_X */ {"NA5", EC_OTHER, EF_UNUSED, 0}, /* PPME_PAGE_FAULT_X */ {"NA5", EC_OTHER, EF_UNUSED, 0},
/* PPME_SYSCALL_EXECVE_19_E */{"execve", EC_PROCESS, EF_MODIFIES_STATE, 1, {{"filename", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_EXECVE_19_E */{"execve", EC_PROCESS, EF_MODIFIES_STATE, 1, {{"filename", PT_FSPATH, PF_NA} } },
/* PPME_SYSCALL_EXECVE_19_X */{"execve", EC_PROCESS, EF_MODIFIES_STATE, 19, {{"res", PT_ERRNO, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_UINT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"comm", PT_CHARBUF, PF_NA}, {"cgroups", PT_BYTEBUF, PF_NA}, {"env", PT_BYTEBUF, PF_NA}, {"tty", PT_INT32, PF_DEC}, {"pgid", PT_PID, PF_DEC}, {"loginuid", PT_INT32, PF_DEC} } }, /* PPME_SYSCALL_EXECVE_19_X */{"execve", EC_PROCESS, EF_MODIFIES_STATE, 20, {{"res", PT_ERRNO, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_UINT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"comm", PT_CHARBUF, PF_NA}, {"cgroups", PT_BYTEBUF, PF_NA}, {"env", PT_BYTEBUF, PF_NA}, {"tty", PT_INT32, PF_DEC}, {"pgid", PT_PID, PF_DEC}, {"loginuid", PT_INT32, PF_DEC}, {"flags", PT_FLAGS32, PF_HEX, execve_flags} } },
/* PPME_SYSCALL_SETPGID_E */{"setpgid", EC_PROCESS, EF_MODIFIES_STATE, 2, {{"pid", PT_PID, PF_DEC}, {"pgid", PT_PID, PF_DEC} } }, /* PPME_SYSCALL_SETPGID_E */{"setpgid", EC_PROCESS, EF_MODIFIES_STATE, 2, {{"pid", PT_PID, PF_DEC}, {"pgid", PT_PID, PF_DEC} } },
/* PPME_SYSCALL_SETPGID_X */{"setpgid", EC_PROCESS, EF_MODIFIES_STATE, 1, {{"res", PT_PID, PF_DEC} } }, /* PPME_SYSCALL_SETPGID_X */{"setpgid", EC_PROCESS, EF_MODIFIES_STATE, 1, {{"res", PT_PID, PF_DEC} } },
/* PPME_SYSCALL_BPF_E */{"bpf", EC_OTHER, EF_CREATES_FD, 1, {{"cmd", PT_INT64, PF_DEC} } }, /* PPME_SYSCALL_BPF_E */{"bpf", EC_OTHER, EF_CREATES_FD, 1, {{"cmd", PT_INT64, PF_DEC} } },
@ -333,7 +333,17 @@ const struct ppm_event_info g_event_info[PPM_EVENT_MAX] = {
/* PPME_SYSCALL_RENAMEAT2_E */{"renameat2", EC_FILE, EF_NONE, 0 }, /* PPME_SYSCALL_RENAMEAT2_E */{"renameat2", EC_FILE, EF_NONE, 0 },
/* PPME_SYSCALL_RENAMEAT2_X */{"renameat2", EC_FILE, EF_NONE, 6, {{"res", PT_ERRNO, PF_DEC}, {"olddirfd", PT_FD, PF_DEC}, {"oldpath", PT_FSRELPATH, PF_NA, DIRFD_PARAM(1)}, {"newdirfd", PT_FD, PF_DEC}, {"newpath", PT_FSRELPATH, PF_NA, DIRFD_PARAM(3)}, {"flags", PT_FLAGS32, PF_HEX, renameat2_flags} } }, /* PPME_SYSCALL_RENAMEAT2_X */{"renameat2", EC_FILE, EF_NONE, 6, {{"res", PT_ERRNO, PF_DEC}, {"olddirfd", PT_FD, PF_DEC}, {"oldpath", PT_FSRELPATH, PF_NA, DIRFD_PARAM(1)}, {"newdirfd", PT_FD, PF_DEC}, {"newpath", PT_FSRELPATH, PF_NA, DIRFD_PARAM(3)}, {"flags", PT_FLAGS32, PF_HEX, renameat2_flags} } },
/* PPME_SYSCALL_USERFAULTFD_E */{"userfaultfd", EC_FILE, EF_CREATES_FD | EF_MODIFIES_STATE, 0}, /* PPME_SYSCALL_USERFAULTFD_E */{"userfaultfd", EC_FILE, EF_CREATES_FD | EF_MODIFIES_STATE, 0},
/* PPME_SYSCALL_USERFAULTFD_X */{"userfaultfd", EC_FILE, EF_CREATES_FD | EF_MODIFIES_STATE, 2, {{"res", PT_ERRNO, PF_DEC}, {"flags", PT_FLAGS32, PF_HEX, file_flags} } } /* PPME_SYSCALL_USERFAULTFD_X */{"userfaultfd", EC_FILE, EF_CREATES_FD | EF_MODIFIES_STATE, 2, {{"res", PT_ERRNO, PF_DEC}, {"flags", PT_FLAGS32, PF_HEX, file_flags} } },
/* PPME_PLUGINEVENT_E */{"pluginevent", EC_OTHER, EF_LARGE_PAYLOAD, 2, {{"plugin ID", PT_UINT32, PF_DEC}, {"event_data", PT_BYTEBUF, PF_NA} } },
/* PPME_NA1 */{"pluginevent", EC_OTHER, EF_UNUSED, 0},
/* PPME_CONTAINER_JSON_2_E */{"container", EC_PROCESS, EF_MODIFIES_STATE | EF_LARGE_PAYLOAD, 1, {{"json", PT_CHARBUF, PF_NA} } },
/* PPME_CONTAINER_JSON_2_X */{"container", EC_PROCESS, EF_UNUSED, 0},
/* PPME_SYSCALL_OPENAT2_E */{"openat2", EC_FILE, EF_CREATES_FD | EF_MODIFIES_STATE, 0},
/* PPME_SYSCALL_OPENAT2_X */{"openat2", EC_FILE, EF_CREATES_FD | EF_MODIFIES_STATE, 6, {{"fd", PT_FD, PF_DEC}, {"dirfd", PT_FD, PF_DEC}, {"name", PT_FSRELPATH, PF_NA, DIRFD_PARAM(1)}, {"flags", PT_FLAGS32, PF_HEX, file_flags}, {"mode", PT_UINT32, PF_OCT}, {"resolve", PT_FLAGS32, PF_HEX, openat2_flags} } },
/* PPME_SYSCALL_MPROTECT_E */{"mprotect", EC_MEMORY, EF_DROP_SIMPLE_CONS, 3, {{"addr", PT_UINT64, PF_HEX}, {"length", PT_UINT64, PF_DEC}, {"prot", PT_FLAGS32, PF_HEX, prot_flags} } },
/* PPME_SYSCALL_MPROTECT_X */{"mprotect", EC_MEMORY, EF_DROP_SIMPLE_CONS, 1, {{"res", PT_ERRNO, PF_DEC} } },
/* PPME_SYSCALL_EXECVEAT_E */{"execveat", EC_PROCESS, EF_MODIFIES_STATE, 3, {{"dirfd", PT_FD, PF_DEC}, {"pathname", PT_FSRELPATH, PF_NA, DIRFD_PARAM(0)}, {"flags", PT_FLAGS32, PF_HEX, execveat_flags} } },
/* PPME_SYSCALL_EXECVEAT_X */{"execveat", EC_PROCESS, EF_MODIFIES_STATE, 19, {{"res", PT_ERRNO, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_UINT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"comm", PT_CHARBUF, PF_NA}, {"cgroups", PT_BYTEBUF, PF_NA}, {"env", PT_BYTEBUF, PF_NA}, {"tty", PT_INT32, PF_DEC}, {"pgid", PT_PID, PF_DEC}, {"loginuid", PT_INT32, PF_DEC} } },
/* NB: Starting from scap version 1.2, event types will no longer be changed when an event is modified, and the only kind of change permitted for pre-existent events is adding parameters. /* NB: Starting from scap version 1.2, event types will no longer be changed when an event is modified, and the only kind of change permitted for pre-existent events is adding parameters.
* New event types are allowed only for new syscalls or new internal events. * New event types are allowed only for new syscalls or new internal events.
* The number of parameters can be used to differentiate between event versions. * The number of parameters can be used to differentiate between event versions.

View File

@ -306,6 +306,12 @@ const struct ppm_event_entry g_ppm_events[PPM_EVENT_MAX] = {
[PPME_SYSCALL_RENAMEAT2_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_RENAMEAT2_E] = {FILLER_REF(sys_empty)},
[PPME_SYSCALL_RENAMEAT2_X] = {FILLER_REF(sys_renameat2_x)}, [PPME_SYSCALL_RENAMEAT2_X] = {FILLER_REF(sys_renameat2_x)},
[PPME_SYSCALL_USERFAULTFD_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_USERFAULTFD_E] = {FILLER_REF(sys_empty)},
[PPME_SYSCALL_USERFAULTFD_X] = {FILLER_REF(sys_autofill), 2, APT_REG, {{AF_ID_RETVAL}, {0} } } [PPME_SYSCALL_USERFAULTFD_X] = {FILLER_REF(sys_autofill), 2, APT_REG, {{AF_ID_RETVAL}, {0} } },
[PPME_SYSCALL_OPENAT2_E] = {FILLER_REF(sys_empty)},
[PPME_SYSCALL_OPENAT2_X] = {FILLER_REF(sys_openat2_x)},
[PPME_SYSCALL_MPROTECT_E] = {FILLER_REF(sys_mprotect_e)},
[PPME_SYSCALL_MPROTECT_X] = {FILLER_REF(sys_mprotect_x)},
[PPME_SYSCALL_EXECVEAT_E] = {FILLER_REF(sys_execveat_e)},
[PPME_SYSCALL_EXECVEAT_X] = {FILLER_REF(proc_startupdate)},
#endif /* WDIG */ #endif /* WDIG */
}; };

View File

@ -525,3 +525,24 @@ const struct ppm_name_value renameat2_flags[] = {
{"RENAME_WHITEOUT", PPM_RENAME_WHITEOUT}, {"RENAME_WHITEOUT", PPM_RENAME_WHITEOUT},
{0, 0}, {0, 0},
}; };
const struct ppm_name_value openat2_flags[] = {
{"RESOLVE_BENEATH", PPM_RESOLVE_BENEATH},
{"RESOLVE_IN_ROOT", PPM_RESOLVE_IN_ROOT},
{"RESOLVE_NO_MAGICLINKS", PPM_RESOLVE_NO_MAGICLINKS},
{"RESOLVE_NO_SYMLINKS", PPM_RESOLVE_NO_SYMLINKS},
{"RESOLVE_NO_XDEV", PPM_RESOLVE_NO_XDEV},
{"RESOLVE_CACHED", PPM_RESOLVE_CACHED},
{0, 0},
};
const struct ppm_name_value execve_flags[] = {
{"EXE_WRITABLE", PPM_EXE_WRITABLE},
{0, 0},
};
const struct ppm_name_value execveat_flags[] = {
{"AT_EMPTY_PATH", PPM_EXVAT_AT_EMPTY_PATH},
{"AT_SYMLINK_NOFOLLOW", PPM_EXVAT_AT_SYMLINK_NOFOLLOW},
{0, 0},
};

View File

@ -154,7 +154,6 @@ TRACEPOINT_PROBE(signal_deliver_probe, int sig, struct siginfo *info, struct k_s
TRACEPOINT_PROBE(page_fault_probe, unsigned long address, struct pt_regs *regs, unsigned long error_code); TRACEPOINT_PROBE(page_fault_probe, unsigned long address, struct pt_regs *regs, unsigned long error_code);
#endif #endif
DECLARE_BITMAP(g_events_mask, PPM_EVENT_MAX);
static struct ppm_device *g_ppm_devs; static struct ppm_device *g_ppm_devs;
static struct class *g_ppm_class; static struct class *g_ppm_class;
static unsigned int g_ppm_numdevs; static unsigned int g_ppm_numdevs;
@ -452,7 +451,7 @@ static int ppm_open(struct inode *inode, struct file *filp)
consumer->fullcapture_port_range_start = 0; consumer->fullcapture_port_range_start = 0;
consumer->fullcapture_port_range_end = 0; consumer->fullcapture_port_range_end = 0;
consumer->statsd_port = PPM_PORT_STATSD; consumer->statsd_port = PPM_PORT_STATSD;
bitmap_fill(g_events_mask, PPM_EVENT_MAX); /* Enable all syscall to be passed to userspace */ bitmap_fill(consumer->events_mask, PPM_EVENT_MAX); /* Enable all syscall to be passed to userspace */
reset_ring_buffer(ring); reset_ring_buffer(ring);
ring->open = true; ring->open = true;
@ -941,11 +940,11 @@ cleanup_ioctl_procinfo:
{ {
vpr_info("PPM_IOCTL_MASK_ZERO_EVENTS, consumer %p\n", consumer_id); vpr_info("PPM_IOCTL_MASK_ZERO_EVENTS, consumer %p\n", consumer_id);
bitmap_zero(g_events_mask, PPM_EVENT_MAX); bitmap_zero(consumer->events_mask, PPM_EVENT_MAX);
/* Used for dropping events so they must stay on */ /* Used for dropping events so they must stay on */
set_bit(PPME_DROP_E, g_events_mask); set_bit(PPME_DROP_E, consumer->events_mask);
set_bit(PPME_DROP_X, g_events_mask); set_bit(PPME_DROP_X, consumer->events_mask);
ret = 0; ret = 0;
goto cleanup_ioctl; goto cleanup_ioctl;
@ -962,7 +961,7 @@ cleanup_ioctl_procinfo:
goto cleanup_ioctl; goto cleanup_ioctl;
} }
set_bit(syscall_to_set, g_events_mask); set_bit(syscall_to_set, consumer->events_mask);
ret = 0; ret = 0;
goto cleanup_ioctl; goto cleanup_ioctl;
@ -979,7 +978,7 @@ cleanup_ioctl_procinfo:
goto cleanup_ioctl; goto cleanup_ioctl;
} }
clear_bit(syscall_to_unset, g_events_mask); clear_bit(syscall_to_unset, consumer->events_mask);
ret = 0; ret = 0;
goto cleanup_ioctl; goto cleanup_ioctl;
@ -1523,13 +1522,15 @@ static inline int drop_event(struct ppm_consumer_t *consumer,
} }
if (consumer->dropping_mode) { if (consumer->dropping_mode) {
nanoseconds ns2 = ns;
if (drop_flags & UF_ALWAYS_DROP) { if (drop_flags & UF_ALWAYS_DROP) {
ASSERT((drop_flags & UF_NEVER_DROP) == 0); ASSERT((drop_flags & UF_NEVER_DROP) == 0);
return 1; return 1;
} }
if (consumer->sampling_interval < SECOND_IN_NS && if (consumer->sampling_interval < SECOND_IN_NS &&
(ns % SECOND_IN_NS) >= consumer->sampling_interval) { /* do_div replaces ns2 with the quotient and returns the remainder */
do_div(ns2, SECOND_IN_NS) >= consumer->sampling_interval) {
if (consumer->is_dropping == 0) { if (consumer->is_dropping == 0) {
consumer->is_dropping = 1; consumer->is_dropping = 1;
record_drop_e(consumer, ns, drop_flags); record_drop_e(consumer, ns, drop_flags);
@ -1585,7 +1586,7 @@ static int record_event_consumer(struct ppm_consumer_t *consumer,
int32_t cbres = PPM_SUCCESS; int32_t cbres = PPM_SUCCESS;
int cpu; int cpu;
if (!test_bit(event_type, g_events_mask)) if (!test_bit(event_type, consumer->events_mask))
return res; return res;
if (event_type != PPME_DROP_E && event_type != PPME_DROP_X) { if (event_type != PPME_DROP_E && event_type != PPME_DROP_X) {

View File

@ -87,6 +87,7 @@ struct ppm_consumer_t {
uint16_t fullcapture_port_range_start; uint16_t fullcapture_port_range_start;
uint16_t fullcapture_port_range_end; uint16_t fullcapture_port_range_end;
uint16_t statsd_port; uint16_t statsd_port;
DECLARE_BITMAP(events_mask, PPM_EVENT_MAX);
}; };
#endif // UDIG #endif // UDIG

View File

@ -356,10 +356,12 @@
#define __NR_ia32_process_vm_writev 348 #define __NR_ia32_process_vm_writev 348
#define __NR_ia32_renameat2 349 #define __NR_ia32_renameat2 349
#define __NR_ia32_userfaultfd 350 #define __NR_ia32_userfaultfd 350
#define __NR_ia32_openat2 351
#define __NR_ia32_execveat 352
#ifdef __KERNEL__ #ifdef __KERNEL__
#define NR_ia32_syscalls 351 #define NR_ia32_syscalls 353
#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_IPC_PARSE_VERSION
#define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_OLD_READDIR

View File

@ -885,6 +885,29 @@ static struct socket *ppm_sockfd_lookup_light(int fd, int *err, int *fput_needed
} }
*/ */
static void unix_socket_path(char *dest, const char *path, size_t size)
{
if (path[0] == '\0') {
/*
* Extract from: https://man7.org/linux/man-pages/man7/unix.7.html
* an abstract socket address is distinguished (from a
* pathname socket) by the fact that sun_path[0] is a null byte
* ('\0'). The socket's address in this namespace is given by
* the additional bytes in sun_path that are covered by the
* specified length of the address structure.
*/
snprintf(dest,
size,
"@%s",
path + 1);
} else {
snprintf(dest,
size,
"%s",
path); /* we assume this will be smaller than (targetbufsize - (1 + 8 + 8)) */
}
}
/* /*
* Convert a sockaddr into our address representation and copy it to * Convert a sockaddr into our address representation and copy it to
* targetbuf * targetbuf
@ -974,11 +997,10 @@ u16 pack_addr(struct sockaddr *usrsockaddr,
size = 1; size = 1;
*targetbuf = socket_family_to_scap((u8)family); *targetbuf = socket_family_to_scap((u8)family);
dest = strncpy(targetbuf + 1,
usrsockaddr_un->sun_path,
UNIX_PATH_MAX); /* we assume this will be smaller than (targetbufsize - (1 + 8 + 8)) */
dest[UNIX_PATH_MAX - 1] = 0; dest = targetbuf + 1;
unix_socket_path(dest, usrsockaddr_un->sun_path, UNIX_PATH_MAX);
size += (u16)strlen(dest) + 1; size += (u16)strlen(dest) + 1;
break; break;
@ -1233,11 +1255,10 @@ u16 fd_to_socktuple(int fd,
} }
ASSERT(us_name); ASSERT(us_name);
dest = strncpy(targetbuf + 1 + 8 + 8,
(char *)us_name,
UNIX_PATH_MAX); /* we assume this will be smaller than (targetbufsize - (1 + 8 + 8)) */
dest[UNIX_PATH_MAX - 1] = 0; dest = targetbuf + 1 + 8 + 8;
unix_socket_path(dest, us_name, UNIX_PATH_MAX);
size += strlen(dest) + 1; size += strlen(dest) + 1;
#endif /* UDIG */ #endif /* UDIG */
break; break;

View File

@ -595,6 +595,27 @@ or GPL2.txt for full copies of the license.
#define PPM_RENAME_EXCHANGE (1 << 1) /* Exchange source and dest */ #define PPM_RENAME_EXCHANGE (1 << 1) /* Exchange source and dest */
#define PPM_RENAME_WHITEOUT (1 << 2) /* Whiteout source */ #define PPM_RENAME_WHITEOUT (1 << 2) /* Whiteout source */
/*
* Openat2 resolve flags
*/
#define PPM_RESOLVE_BENEATH (1 << 0)
#define PPM_RESOLVE_IN_ROOT (1 << 1)
#define PPM_RESOLVE_NO_MAGICLINKS (1 << 2)
#define PPM_RESOLVE_NO_SYMLINKS (1 << 3)
#define PPM_RESOLVE_NO_XDEV (1 << 4)
#define PPM_RESOLVE_CACHED (1 << 5)
/*
* Execve additional flags
*/
#define PPM_EXE_WRITABLE (1 << 0)
/*
* Execveat flags
*/
#define PPM_EXVAT_AT_EMPTY_PATH (1 << 0) /* If pathname is an empty string, operate on the file referred to by dirfd */
#define PPM_EXVAT_AT_SYMLINK_NOFOLLOW (1 << 1) /* If the file is a symbolic link, then the call fails */
/* /*
* SuS says limits have to be unsigned. * SuS says limits have to be unsigned.
* Which makes a ton more sense anyway. * Which makes a ton more sense anyway.
@ -961,7 +982,17 @@ enum ppm_event_type {
PPME_SYSCALL_RENAMEAT2_X = 319, PPME_SYSCALL_RENAMEAT2_X = 319,
PPME_SYSCALL_USERFAULTFD_E = 320, PPME_SYSCALL_USERFAULTFD_E = 320,
PPME_SYSCALL_USERFAULTFD_X = 321, PPME_SYSCALL_USERFAULTFD_X = 321,
PPM_EVENT_MAX = 322 PPME_PLUGINEVENT_E = 322,
PPME_PLUGINEVENT_X = 323,
PPME_CONTAINER_JSON_2_E = 324,
PPME_CONTAINER_JSON_2_X = 325,
PPME_SYSCALL_OPENAT2_E = 326,
PPME_SYSCALL_OPENAT2_X = 327,
PPME_SYSCALL_MPROTECT_E = 328,
PPME_SYSCALL_MPROTECT_X = 329,
PPME_SYSCALL_EXECVEAT_E = 330,
PPME_SYSCALL_EXECVEAT_X = 331,
PPM_EVENT_MAX = 332
}; };
/*@}*/ /*@}*/
@ -1291,7 +1322,11 @@ enum ppm_syscall_code {
PPM_SC_FADVISE64 = 319, PPM_SC_FADVISE64 = 319,
PPM_SC_RENAMEAT2 = 320, PPM_SC_RENAMEAT2 = 320,
PPM_SC_USERFAULTFD = 321, PPM_SC_USERFAULTFD = 321,
PPM_SC_MAX = 322, PPM_SC_OPENAT2 = 322,
PPM_SC_UMOUNT2 = 323,
PPM_SC_EXECVE = 324,
PPM_SC_EXECVEAT = 325,
PPM_SC_MAX = 326,
}; };
/* /*
@ -1332,7 +1367,8 @@ enum ppm_event_flags {
EF_WAITS = (1 << 7), /* This event reads data from an FD. */ EF_WAITS = (1 << 7), /* This event reads data from an FD. */
EF_SKIPPARSERESET = (1 << 8), /* This event shouldn't pollute the parser lastevent state tracker. */ EF_SKIPPARSERESET = (1 << 8), /* This event shouldn't pollute the parser lastevent state tracker. */
EF_OLD_VERSION = (1 << 9), /* This event is kept for backward compatibility */ EF_OLD_VERSION = (1 << 9), /* This event is kept for backward compatibility */
EF_DROP_SIMPLE_CONS = (1 << 10) /* This event can be skipped by consumers that privilege low overhead to full event capture */ EF_DROP_SIMPLE_CONS = (1 << 10), /* This event can be skipped by consumers that privilege low overhead to full event capture */
EF_LARGE_PAYLOAD = (1 << 11), /* This event has a large payload, ie: up to UINT32_MAX bytes. DO NOT USE ON syscalls-driven events!!! */
}; };
/* /*
@ -1518,6 +1554,9 @@ extern const struct ppm_name_value unlinkat_flags[];
extern const struct ppm_name_value linkat_flags[]; extern const struct ppm_name_value linkat_flags[];
extern const struct ppm_name_value chmod_mode[]; extern const struct ppm_name_value chmod_mode[];
extern const struct ppm_name_value renameat2_flags[]; extern const struct ppm_name_value renameat2_flags[];
extern const struct ppm_name_value openat2_flags[];
extern const struct ppm_name_value execve_flags[];
extern const struct ppm_name_value execveat_flags[];
extern const struct ppm_param_info sockopt_dynamic_param[]; extern const struct ppm_param_info sockopt_dynamic_param[];
extern const struct ppm_param_info ptrace_dynamic_param[]; extern const struct ppm_param_info ptrace_dynamic_param[];
@ -1552,8 +1591,10 @@ enum syscall_flags {
UF_USED = (1 << 0), UF_USED = (1 << 0),
UF_NEVER_DROP = (1 << 1), UF_NEVER_DROP = (1 << 1),
UF_ALWAYS_DROP = (1 << 2), UF_ALWAYS_DROP = (1 << 2),
UF_SIMPLEDRIVER_KEEP = (1 << 3), UF_SIMPLEDRIVER_KEEP = (1 << 3), ///< Mark a syscall to be kept in simpledriver mode, see scap_enable_simpledriver_mode()
UF_ATOMIC = (1 << 4), ///< The handler should not block (interrupt context) UF_ATOMIC = (1 << 4), ///< The handler should not block (interrupt context)
UF_UNINTERESTING = (1 << 5), ///< Marks a syscall as not interesting. Currently only used by BPF probe to avoid tracing uninteresting syscalls.
///< Kmod uses a different logic path as we communicate with it through ioctls
}; };
struct syscall_evt_pair { struct syscall_evt_pair {

View File

@ -95,6 +95,8 @@ or GPL2.txt for full copies of the license.
#endif #endif
#include "kernel_hacks.h" #include "kernel_hacks.h"
#include "systype_compat.h"
#endif /* UDIG */ #endif /* UDIG */
#define merge_64(hi, lo) ((((unsigned long long)(hi)) << 32) + ((lo) & 0xffffffffUL)) #define merge_64(hi, lo) ((((unsigned long long)(hi)) << 32) + ((lo) & 0xffffffffUL))
@ -379,6 +381,33 @@ int f_sys_write_x(struct event_filler_arguments *args)
#ifndef UDIG #ifndef UDIG
/*
* get_mm_exe_file is only exported in some kernel versions
*/
struct file *ppm_get_mm_exe_file(struct mm_struct *mm)
{
struct file *exe_file;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
rcu_read_lock();
exe_file = rcu_dereference(mm->exe_file);
if (exe_file && !get_file_rcu(exe_file))
exe_file = NULL;
rcu_read_unlock();
#else
/* We need mmap_sem to protect against races with removal of
* VM_EXECUTABLE vmas */
down_read(&mm->mmap_sem);
exe_file = mm->exe_file;
if (exe_file)
get_file(exe_file);
up_read(&mm->mmap_sem);
#endif
return exe_file;
}
/* /*
* get_mm_counter was not inline and exported between 3.0 and 3.4 * get_mm_counter was not inline and exported between 3.0 and 3.4
* https://github.com/torvalds/linux/commit/69c978232aaa99476f9bd002c2a29a84fa3779b5 * https://github.com/torvalds/linux/commit/69c978232aaa99476f9bd002c2a29a84fa3779b5
@ -739,7 +768,8 @@ int f_proc_startupdate(struct event_filler_arguments *args)
return res; return res;
if (unlikely(retval < 0 && if (unlikely(retval < 0 &&
args->event_type != PPME_SYSCALL_EXECVE_19_X)) { args->event_type != PPME_SYSCALL_EXECVE_19_X &&
args->event_type != PPME_SYSCALL_EXECVEAT_X)) {
/* The call failed, but this syscall has no exe, args /* The call failed, but this syscall has no exe, args
* anyway, so I report empty ones */ * anyway, so I report empty ones */
@ -793,13 +823,25 @@ int f_proc_startupdate(struct event_filler_arguments *args)
} else { } else {
/* /*
* The execve call failed. I get exe, args from the * The execve or execveat call failed. I get exe, args from the
* input args; put one \0-separated exe-args string into * input args; put one \0-separated exe-args string into
* str_storage * str_storage
*/ */
args->str_storage[0] = 0; args->str_storage[0] = 0;
switch (args->event_type)
{
case PPME_SYSCALL_EXECVE_19_X:
syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val);
break;
case PPME_SYSCALL_EXECVEAT_X:
syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val);
default:
val = 0;
break;
}
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
if (unlikely(args->compat)) if (unlikely(args->compat))
args_len = compat_accumulate_argv_or_env((compat_uptr_t)val, args_len = compat_accumulate_argv_or_env((compat_uptr_t)val,
@ -1028,12 +1070,16 @@ cgroups_error:
if (unlikely(res != PPM_SUCCESS)) if (unlikely(res != PPM_SUCCESS))
return res; return res;
} else if (args->event_type == PPME_SYSCALL_EXECVE_19_X) { } else if (args->event_type == PPME_SYSCALL_EXECVE_19_X ||
args->event_type == PPME_SYSCALL_EXECVEAT_X) {
/* /*
* execve-only parameters * execve-only parameters
*/ */
long env_len = 0; long env_len = 0;
int tty_nr = 0; int tty_nr = 0;
bool exe_writable = false;
struct file *exe_file = NULL;
uint32_t flags = 0; // execve additional flags
if (likely(retval >= 0)) { if (likely(retval >= 0)) {
/* /*
@ -1054,7 +1100,20 @@ cgroups_error:
/* /*
* The call failed, so get the env from the arguments * The call failed, so get the env from the arguments
*/ */
switch (args->event_type)
{
case PPME_SYSCALL_EXECVE_19_X:
syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val); syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val);
break;
case PPME_SYSCALL_EXECVEAT_X:
syscall_get_arguments_deprecated(current, args->regs, 3, 1, &val);
break;
default:
val = 0;
break;
}
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
if (unlikely(args->compat)) if (unlikely(args->compat))
env_len = compat_accumulate_argv_or_env((compat_uptr_t)val, env_len = compat_accumulate_argv_or_env((compat_uptr_t)val,
@ -1110,6 +1169,38 @@ cgroups_error:
res = val_to_ring(args, val, 0, false, 0); res = val_to_ring(args, val, 0, false, 0);
if (unlikely(res != PPM_SUCCESS)) if (unlikely(res != PPM_SUCCESS))
return res; return res;
/*
* exe_writable flag
*/
exe_file = ppm_get_mm_exe_file(mm);
if (exe_file != NULL) {
if (file_inode(exe_file) != NULL) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
exe_writable |= (inode_permission(current_user_ns(), file_inode(exe_file), MAY_WRITE) == 0);
exe_writable |= inode_owner_or_capable(current_user_ns(), file_inode(exe_file));
#else
exe_writable |= (inode_permission(file_inode(exe_file), MAY_WRITE) == 0);
exe_writable |= inode_owner_or_capable(file_inode(exe_file));
#endif
}
fput(exe_file);
}
if (exe_writable) {
flags |= PPM_EXE_WRITABLE;
}
/*
* flags
* Write all the additional flags for execve
*/
res = val_to_ring(args, flags, 0, false, 0);
if (unlikely(res != PPM_SUCCESS))
return res;
} }
return add_sentinel(args); return add_sentinel(args);
@ -1135,6 +1226,58 @@ int f_sys_execve_e(struct event_filler_arguments *args)
return add_sentinel(args); return add_sentinel(args);
} }
int f_sys_execveat_e(struct event_filler_arguments *args)
{
int res;
syscall_arg_t val;
unsigned long flags;
/*
* dirfd
*/
syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val);
if ((int)val == AT_FDCWD)
{
val = PPM_AT_FDCWD;
}
res = val_to_ring(args, val, 0, false, 0);
if (unlikely(res != PPM_SUCCESS))
{
return res;
}
/*
* pathname
*/
syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val);
res = val_to_ring(args, val, 0, true, 0);
if (unlikely(res == PPM_FAILURE_INVALID_USER_MEMORY))
{
res = val_to_ring(args, (unsigned long)"<NA>", 0, false, 0);
}
if (unlikely(res != PPM_SUCCESS))
{
return res;
}
/*
* flags
*/
syscall_get_arguments_deprecated(current, args->regs, 4, 1, &val);
flags = execveat_flags_to_scap(val);
res = val_to_ring(args, flags, 0, false, 0);
if (unlikely(res != PPM_SUCCESS))
{
return res;
}
return add_sentinel(args);
}
int f_sys_socket_bind_x(struct event_filler_arguments *args) int f_sys_socket_bind_x(struct event_filler_arguments *args)
{ {
int res; int res;
@ -4026,6 +4169,51 @@ int f_sys_mmap_e(struct event_filler_arguments *args)
return add_sentinel(args); return add_sentinel(args);
} }
int f_sys_mprotect_e(struct event_filler_arguments *args)
{
unsigned long val;
int res;
/*
* addr
*/
syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val);
res = val_to_ring(args, val, 0, false, 0);
if (unlikely(res != PPM_SUCCESS))
return res;
/*
* length
*/
syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val);
res = val_to_ring(args, val, 0, false, 0);
if (unlikely(res != PPM_SUCCESS))
return res;
/*
* prot
*/
syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val);
res = val_to_ring(args, prot_flags_to_scap(val), 0, false, 0);
if (unlikely(res != PPM_SUCCESS))
return res;
return add_sentinel(args);
}
int f_sys_mprotect_x(struct event_filler_arguments *args)
{
int res;
int64_t retval;
retval = (int64_t)syscall_get_return_value(current, args->regs);
res = val_to_ring(args, retval, 0, false, 0);
if (unlikely(res != PPM_SUCCESS))
return res;
return add_sentinel(args);
}
int f_sys_renameat_x(struct event_filler_arguments *args) int f_sys_renameat_x(struct event_filler_arguments *args)
{ {
unsigned long val; unsigned long val;
@ -4184,6 +4372,88 @@ int f_sys_symlinkat_x(struct event_filler_arguments *args)
return add_sentinel(args); return add_sentinel(args);
} }
int f_sys_openat2_x(struct event_filler_arguments *args)
{
unsigned long resolve;
unsigned long flags;
unsigned long val;
unsigned long mode;
int res;
int64_t retval;
#ifdef __NR_openat2
struct open_how how;
#endif
retval = (int64_t)syscall_get_return_value(current, args->regs);
res = val_to_ring(args, retval, 0, false, 0);
if (unlikely(res != PPM_SUCCESS))
return res;
/*
* dirfd
*/
syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val);
if ((int)val == AT_FDCWD)
val = PPM_AT_FDCWD;
res = val_to_ring(args, val, 0, false, 0);
if (unlikely(res != PPM_SUCCESS))
return res;
/*
* name
*/
syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val);
res = val_to_ring(args, val, 0, true, 0);
if (unlikely(res != PPM_SUCCESS))
return res;
#ifdef __NR_openat2
/*
* how: we get the data structure, and put its fields in the buffer one by one
*/
syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val);
res = ppm_copy_from_user(&how, (void *)val, sizeof(struct open_how));
if (unlikely(res != 0))
return PPM_FAILURE_INVALID_USER_MEMORY;
flags = open_flags_to_scap(how.flags);
mode = open_modes_to_scap(how.flags, how.mode);
resolve = openat2_resolve_to_scap(how.resolve);
#else
flags = 0;
mode = 0;
resolve = 0;
#endif
/*
* flags (extracted from open_how structure)
* Note that we convert them into the ppm portable representation before pushing them to the ring
*/
res = val_to_ring(args, flags, 0, true, 0);
if (unlikely(res != PPM_SUCCESS))
return res;
/*
* mode (extracted from open_how structure)
* Note that we convert them into the ppm portable representation before pushing them to the ring
*/
res = val_to_ring(args, mode, 0, true, 0);
if (unlikely(res != PPM_SUCCESS))
return res;
/*
* resolve (extracted from open_how structure)
* Note that we convert them into the ppm portable representation before pushing them to the ring
*/
res = val_to_ring(args, resolve, 0, true, 0);
if (unlikely(res != PPM_SUCCESS))
return res;
return add_sentinel(args);
}
#endif /* WDIG */ #endif /* WDIG */
int f_sys_procexit_e(struct event_filler_arguments *args) int f_sys_procexit_e(struct event_filler_arguments *args)
@ -4201,9 +4471,39 @@ int f_sys_procexit_e(struct event_filler_arguments *args)
* status * status
*/ */
#ifndef UDIG #ifndef UDIG
/* Exit status */
res = val_to_ring(args, args->sched_prev->exit_code, 0, false, 0); res = val_to_ring(args, args->sched_prev->exit_code, 0, false, 0);
if (unlikely(res != PPM_SUCCESS))
return res;
/* Ret code */
res = val_to_ring(args, __WEXITSTATUS(args->sched_prev->exit_code), 0, false, 0);
if (unlikely(res != PPM_SUCCESS))
return res;
/* If signaled -> signum, else 0 */
if (__WIFSIGNALED(args->sched_prev->exit_code))
{
res = val_to_ring(args, __WTERMSIG(args->sched_prev->exit_code), 0, false, 0);
} else {
res = val_to_ring(args, 0, 0, false, 0);
}
if (unlikely(res != PPM_SUCCESS))
return res;
/* Did it produce a core? */
res = val_to_ring(args, __WCOREDUMP(args->sched_prev->exit_code) != 0, 0, false, 0);
#else #else
res = val_to_ring(args, 0, 0, false, 0); res = val_to_ring(args, 0, 0, false, 0);
if (unlikely(res != PPM_SUCCESS))
return res;
res = val_to_ring(args, 0, 0, false, 0);
if (unlikely(res != PPM_SUCCESS))
return res;
res = val_to_ring(args, 0, 0, false, 0);
if (unlikely(res != PPM_SUCCESS))
return res;
res = val_to_ring(args, 0, 0, false, 0);
#endif #endif
if (unlikely(res != PPM_SUCCESS)) if (unlikely(res != PPM_SUCCESS))
return res; return res;

View File

@ -114,7 +114,12 @@ or GPL2.txt for full copies of the license.
FN(sys_fchmod_x) \ FN(sys_fchmod_x) \
FN(sys_mkdirat_x) \ FN(sys_mkdirat_x) \
FN(sys_openat_x) \ FN(sys_openat_x) \
FN(sys_openat2_x) \
FN(sys_linkat_x) \ FN(sys_linkat_x) \
FN(sys_mprotect_e) \
FN(sys_mprotect_x) \
FN(sys_execveat_e) \
FN(execve_family_flags) \
FN(terminate_filler) FN(terminate_filler)
#define FILLER_ENUM_FN(x) PPM_FILLER_##x, #define FILLER_ENUM_FN(x) PPM_FILLER_##x,

View File

@ -160,6 +160,41 @@ static __always_inline u32 open_modes_to_scap(unsigned long flags,
if (modes & S_ISVTX) if (modes & S_ISVTX)
res |= PPM_S_ISVTX; res |= PPM_S_ISVTX;
return res;
}
static __always_inline u32 openat2_resolve_to_scap(unsigned long flags)
{
u32 res = 0;
#ifdef RESOLVE_NO_XDEV
if (flags & RESOLVE_NO_XDEV)
res |= PPM_RESOLVE_NO_XDEV;
#endif
#ifdef RESOLVE_NO_MAGICLINKS
if (flags & RESOLVE_NO_MAGICLINKS)
res |= PPM_RESOLVE_NO_MAGICLINKS;
#endif
#ifdef RESOLVE_NO_SYMLINKS
if (flags & RESOLVE_NO_SYMLINKS)
res |= PPM_RESOLVE_NO_SYMLINKS;
#endif
#ifdef RESOLVE_BENEATH
if (flags & RESOLVE_BENEATH)
res |= PPM_RESOLVE_BENEATH;
#endif
#ifdef RESOLVE_IN_ROOT
if (flags & RESOLVE_IN_ROOT)
res |= PPM_RESOLVE_IN_ROOT;
#endif
#ifdef RESOLVE_CACHED
if (flags & RESOLVE_CACHED)
res |= PPM_RESOLVE_CACHED;
#endif
return res; return res;
#endif // WDIG #endif // WDIG
} }
@ -830,6 +865,20 @@ static __always_inline u8 sockopt_optname_to_scap(int level, int optname)
#ifdef SO_COOKIE #ifdef SO_COOKIE
case SO_COOKIE: case SO_COOKIE:
return PPM_SOCKOPT_SO_COOKIE; return PPM_SOCKOPT_SO_COOKIE;
#endif
#ifdef __BPF_TRACING__
case INT_MAX:
// forcefully disable switch jump table (clang-5 bug?)
// Basically, when labels values are similar AND the switch has many labels,
// compiler tends to build a jump table as optimization.
// This breaks with eBPF, and in our Makefile we already have the -fno-jump-tables;
// most probably clang5 had some kind of bug that caused -O2 mode to still use jump tables.
// Let's add a "very distant" label value to forcefully disable jump table.
//
// DO NOT merge with below default case
// otherwise this label will be skipped by compiler.
ASSERT(false);
return PPM_SOCKOPT_UNKNOWN;
#endif #endif
default: default:
ASSERT(false); ASSERT(false);
@ -1212,6 +1261,10 @@ static __always_inline u32 semctl_cmd_to_scap(unsigned cmd)
case GETZCNT: return PPM_GETZCNT; case GETZCNT: return PPM_GETZCNT;
case SETALL: return PPM_SETALL; case SETALL: return PPM_SETALL;
case SETVAL: return PPM_SETVAL; case SETVAL: return PPM_SETVAL;
#ifdef __BPF_TRACING__
// forcefully disable switch jump table, see sockopt_optname_to_scap() for more info
case INT_MAX: return 0;
#endif
} }
return 0; return 0;
} }
@ -1347,6 +1400,23 @@ static __always_inline u16 ptrace_requests_to_scap(unsigned long req)
} }
} }
static __always_inline u32 execveat_flags_to_scap(unsigned long flags)
{
u32 res = 0;
#ifdef AT_EMPTY_PATH
if (flags & AT_EMPTY_PATH)
res |= PPM_EXVAT_AT_EMPTY_PATH;
#endif
#ifdef AT_SYMLINK_NOFOLLOW
if (flags & AT_SYMLINK_NOFOLLOW)
res |= PPM_EXVAT_AT_SYMLINK_NOFOLLOW;
#endif
return res;
}
static __always_inline u32 unlinkat_flags_to_scap(unsigned long flags) static __always_inline u32 unlinkat_flags_to_scap(unsigned long flags)
{ {
u32 res = 0; u32 res = 0;

View File

@ -358,6 +358,15 @@ const struct syscall_evt_pair g_syscall_table[SYSCALL_TABLE_SIZE] = {
#ifdef __NR_userfaultfd #ifdef __NR_userfaultfd
[__NR_userfaultfd - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_USERFAULTFD_E, PPME_SYSCALL_USERFAULTFD_X}, [__NR_userfaultfd - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_USERFAULTFD_E, PPME_SYSCALL_USERFAULTFD_X},
#endif #endif
#ifdef __NR_openat2
[__NR_openat2 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_OPENAT2_E, PPME_SYSCALL_OPENAT2_X},
#endif
#ifdef __NR_mprotect
[__NR_mprotect - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_MPROTECT_E, PPME_SYSCALL_MPROTECT_X},
#endif
#ifdef __NR_execveat
[__NR_execveat - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP | UF_SIMPLEDRIVER_KEEP, PPME_SYSCALL_EXECVEAT_E, PPME_SYSCALL_EXECVEAT_X},
#endif
}; };
/* /*
@ -368,6 +377,7 @@ const enum ppm_syscall_code g_syscall_code_routing_table[SYSCALL_TABLE_SIZE] = {
[__NR_exit - SYSCALL_TABLE_ID0] = PPM_SC_EXIT, [__NR_exit - SYSCALL_TABLE_ID0] = PPM_SC_EXIT,
[__NR_read - SYSCALL_TABLE_ID0] = PPM_SC_READ, [__NR_read - SYSCALL_TABLE_ID0] = PPM_SC_READ,
[__NR_write - SYSCALL_TABLE_ID0] = PPM_SC_WRITE, [__NR_write - SYSCALL_TABLE_ID0] = PPM_SC_WRITE,
[__NR_execve - SYSCALL_TABLE_ID0] = PPM_SC_EXECVE,
#ifdef __NR_open #ifdef __NR_open
[__NR_open - SYSCALL_TABLE_ID0] = PPM_SC_OPEN, [__NR_open - SYSCALL_TABLE_ID0] = PPM_SC_OPEN,
#endif #endif
@ -398,6 +408,7 @@ const enum ppm_syscall_code g_syscall_code_routing_table[SYSCALL_TABLE_SIZE] = {
[__NR_lseek - SYSCALL_TABLE_ID0] = PPM_SC_LSEEK, [__NR_lseek - SYSCALL_TABLE_ID0] = PPM_SC_LSEEK,
[__NR_getpid - SYSCALL_TABLE_ID0] = PPM_SC_GETPID, [__NR_getpid - SYSCALL_TABLE_ID0] = PPM_SC_GETPID,
[__NR_mount - SYSCALL_TABLE_ID0] = PPM_SC_MOUNT, [__NR_mount - SYSCALL_TABLE_ID0] = PPM_SC_MOUNT,
[__NR_umount2 - SYSCALL_TABLE_ID0] = PPM_SC_UMOUNT2,
/* [__NR_oldumount - SYSCALL_TABLE_ID0] = PPM_SC_NR_OLDUMOUNT, */ /* [__NR_oldumount - SYSCALL_TABLE_ID0] = PPM_SC_NR_OLDUMOUNT, */
/* [__NR_setuid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_SETUID16, */ /* [__NR_setuid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_SETUID16, */
/* [__NR_getuid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_GETUID16, */ /* [__NR_getuid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_GETUID16, */
@ -977,6 +988,12 @@ const enum ppm_syscall_code g_syscall_code_routing_table[SYSCALL_TABLE_SIZE] = {
#ifdef __NR_userfaultfd #ifdef __NR_userfaultfd
[__NR_userfaultfd - SYSCALL_TABLE_ID0] = PPM_SC_USERFAULTFD, [__NR_userfaultfd - SYSCALL_TABLE_ID0] = PPM_SC_USERFAULTFD,
#endif #endif
#ifdef __NR_openat2
[__NR_openat2 - SYSCALL_TABLE_ID0] = PPM_SC_OPENAT2,
#endif
#ifdef __NR_execveat
[__NR_execveat - SYSCALL_TABLE_ID0] = PPM_SC_EXECVEAT,
#endif
}; };
#ifdef CONFIG_IA32_EMULATION #ifdef CONFIG_IA32_EMULATION
@ -1216,6 +1233,15 @@ const struct syscall_evt_pair g_syscall_ia32_table[SYSCALL_TABLE_SIZE] = {
#ifdef __NR_ia32_userfaultfd #ifdef __NR_ia32_userfaultfd
[__NR_ia32_userfaultfd - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_USERFAULTFD_E, PPME_SYSCALL_USERFAULTFD_X}, [__NR_ia32_userfaultfd - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_USERFAULTFD_E, PPME_SYSCALL_USERFAULTFD_X},
#endif #endif
#ifdef __NR_ia32_openat2
[__NR_ia32_openat2 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_OPENAT2_E, PPME_SYSCALL_OPENAT2_X},
#endif
#ifdef __NR_ia32_mprotect
[__NR_ia32_mprotect - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_MPROTECT_E, PPME_SYSCALL_MPROTECT_X},
#endif
#ifdef __NR_ia32_execveat
[__NR_ia32_execveat - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP | UF_SIMPLEDRIVER_KEEP, PPME_SYSCALL_EXECVEAT_E, PPME_SYSCALL_EXECVEAT_X},
#endif
}; };
/* /*
@ -1226,6 +1252,7 @@ const enum ppm_syscall_code g_syscall_ia32_code_routing_table[SYSCALL_TABLE_SIZE
[__NR_ia32_exit - SYSCALL_TABLE_ID0] = PPM_SC_EXIT, [__NR_ia32_exit - SYSCALL_TABLE_ID0] = PPM_SC_EXIT,
[__NR_ia32_read - SYSCALL_TABLE_ID0] = PPM_SC_READ, [__NR_ia32_read - SYSCALL_TABLE_ID0] = PPM_SC_READ,
[__NR_ia32_write - SYSCALL_TABLE_ID0] = PPM_SC_WRITE, [__NR_ia32_write - SYSCALL_TABLE_ID0] = PPM_SC_WRITE,
[__NR_ia32_execve - SYSCALL_TABLE_ID0] = PPM_SC_EXECVE,
[__NR_ia32_open - SYSCALL_TABLE_ID0] = PPM_SC_OPEN, [__NR_ia32_open - SYSCALL_TABLE_ID0] = PPM_SC_OPEN,
[__NR_ia32_close - SYSCALL_TABLE_ID0] = PPM_SC_CLOSE, [__NR_ia32_close - SYSCALL_TABLE_ID0] = PPM_SC_CLOSE,
[__NR_ia32_creat - SYSCALL_TABLE_ID0] = PPM_SC_CREAT, [__NR_ia32_creat - SYSCALL_TABLE_ID0] = PPM_SC_CREAT,
@ -1240,6 +1267,7 @@ const enum ppm_syscall_code g_syscall_ia32_code_routing_table[SYSCALL_TABLE_SIZE
[__NR_ia32_lseek - SYSCALL_TABLE_ID0] = PPM_SC_LSEEK, [__NR_ia32_lseek - SYSCALL_TABLE_ID0] = PPM_SC_LSEEK,
[__NR_ia32_getpid - SYSCALL_TABLE_ID0] = PPM_SC_GETPID, [__NR_ia32_getpid - SYSCALL_TABLE_ID0] = PPM_SC_GETPID,
[__NR_ia32_mount - SYSCALL_TABLE_ID0] = PPM_SC_MOUNT, [__NR_ia32_mount - SYSCALL_TABLE_ID0] = PPM_SC_MOUNT,
[__NR_ia32_umount2 - SYSCALL_TABLE_ID0] = PPM_SC_UMOUNT2,
/* [__NR_ia32_oldumount - SYSCALL_TABLE_ID0] = PPM_SC_NR_OLDUMOUNT, */ /* [__NR_ia32_oldumount - SYSCALL_TABLE_ID0] = PPM_SC_NR_OLDUMOUNT, */
/* [__NR_ia32_setuid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_SETUID16, */ /* [__NR_ia32_setuid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_SETUID16, */
/* [__NR_ia32_getuid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_GETUID16, */ /* [__NR_ia32_getuid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_GETUID16, */
@ -1388,8 +1416,6 @@ const enum ppm_syscall_code g_syscall_ia32_code_routing_table[SYSCALL_TABLE_SIZE
[__NR_ia32_setresgid - SYSCALL_TABLE_ID0] = PPM_SC_SETRESGID, [__NR_ia32_setresgid - SYSCALL_TABLE_ID0] = PPM_SC_SETRESGID,
[__NR_ia32_getresgid - SYSCALL_TABLE_ID0] = PPM_SC_GETRESGID, [__NR_ia32_getresgid - SYSCALL_TABLE_ID0] = PPM_SC_GETRESGID,
[__NR_ia32_chown - SYSCALL_TABLE_ID0] = PPM_SC_CHOWN, [__NR_ia32_chown - SYSCALL_TABLE_ID0] = PPM_SC_CHOWN,
[__NR_ia32_setuid - SYSCALL_TABLE_ID0] = PPM_SC_SETUID,
[__NR_ia32_setgid - SYSCALL_TABLE_ID0] = PPM_SC_SETGID,
[__NR_ia32_setfsuid - SYSCALL_TABLE_ID0] = PPM_SC_SETFSUID, [__NR_ia32_setfsuid - SYSCALL_TABLE_ID0] = PPM_SC_SETFSUID,
[__NR_ia32_setfsgid - SYSCALL_TABLE_ID0] = PPM_SC_SETFSGID, [__NR_ia32_setfsgid - SYSCALL_TABLE_ID0] = PPM_SC_SETFSGID,
[__NR_ia32_pivot_root - SYSCALL_TABLE_ID0] = PPM_SC_PIVOT_ROOT, [__NR_ia32_pivot_root - SYSCALL_TABLE_ID0] = PPM_SC_PIVOT_ROOT,
@ -1557,9 +1583,6 @@ const enum ppm_syscall_code g_syscall_ia32_code_routing_table[SYSCALL_TABLE_SIZE
#ifdef __NR_ia32_semctl #ifdef __NR_ia32_semctl
[__NR_ia32_semctl - SYSCALL_TABLE_ID0] = PPM_SC_SEMCTL, [__NR_ia32_semctl - SYSCALL_TABLE_ID0] = PPM_SC_SEMCTL,
#endif #endif
#ifdef __NR_ia32_semget
[__NR_ia32_semget - SYSCALL_TABLE_ID0] = PPM_SC_SEMGET,
#endif
#ifdef __NR_ia32_msgsnd #ifdef __NR_ia32_msgsnd
[__NR_ia32_msgsnd - SYSCALL_TABLE_ID0] = PPM_SC_MSGSND, [__NR_ia32_msgsnd - SYSCALL_TABLE_ID0] = PPM_SC_MSGSND,
#endif #endif
@ -1775,6 +1798,12 @@ const enum ppm_syscall_code g_syscall_ia32_code_routing_table[SYSCALL_TABLE_SIZE
#ifdef __NR_ia32_userfaultfd #ifdef __NR_ia32_userfaultfd
[__NR_ia32_userfaultfd - SYSCALL_TABLE_ID0] = PPM_SC_USERFAULTFD, [__NR_ia32_userfaultfd - SYSCALL_TABLE_ID0] = PPM_SC_USERFAULTFD,
#endif #endif
#ifdef __NR_ia32_openat2
[__NR_ia32_openat2 - SYSCALL_TABLE_ID0] = PPM_SC_OPENAT2,
#endif
#ifdef __NR_ia32_execveat
[__NR_ia32_execveat - SYSCALL_TABLE_ID0] = PPM_SC_EXECVEAT,
#endif
}; };
#endif /* CONFIG_IA32_EMULATION */ #endif /* CONFIG_IA32_EMULATION */

14
driver/systype_compat.h Normal file
View File

@ -0,0 +1,14 @@
/* If WIFEXITED(STATUS), the low-order 8 bits of the status. */
#define __WEXITSTATUS(status) (((status) & 0xff00) >> 8)
/* If WIFSIGNALED(STATUS), the terminating signal. */
#define __WTERMSIG(status) ((status) & 0x7f)
/* Nonzero if STATUS indicates termination by a signal. */
#define __WIFSIGNALED(status) \
(((signed char) (((status) & 0x7f) + 1) >> 1) > 0)
/* Nonzero if STATUS indicates the child dumped core. */
#define __WCOREDUMP(status) ((status) & __WCOREFLAG)
#define __WCOREFLAG 0x80

View File

@ -95,7 +95,7 @@ void lua_stackdump(lua_State *L)
// Lua callbacks // Lua callbacks
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#ifdef HAS_LUA_CHISELS #ifdef HAS_LUA_CHISELS
const static struct luaL_reg ll_sysdig [] = const static struct luaL_Reg ll_sysdig [] =
{ {
{"set_filter", &lua_cbacks::set_global_filter}, {"set_filter", &lua_cbacks::set_global_filter},
{"set_snaplen", &lua_cbacks::set_snaplen}, {"set_snaplen", &lua_cbacks::set_snaplen},
@ -131,7 +131,7 @@ const static struct luaL_reg ll_sysdig [] =
{NULL,NULL} {NULL,NULL}
}; };
const static struct luaL_reg ll_chisel [] = const static struct luaL_Reg ll_chisel [] =
{ {
{"request_field", &lua_cbacks::request_field}, {"request_field", &lua_cbacks::request_field},
{"set_filter", &lua_cbacks::set_filter}, {"set_filter", &lua_cbacks::set_filter},
@ -143,7 +143,7 @@ const static struct luaL_reg ll_chisel [] =
{NULL,NULL} {NULL,NULL}
}; };
const static struct luaL_reg ll_evt [] = const static struct luaL_Reg ll_evt [] =
{ {
{"field", &lua_cbacks::field}, {"field", &lua_cbacks::field},
{"get_num", &lua_cbacks::get_num}, {"get_num", &lua_cbacks::get_num},

View File

@ -39,7 +39,7 @@ typedef struct lua_State lua_State;
*/ */
/*! /*!
\brief This is the class that compiles and runs sysdig-type filters. \brief This is the class that compiles and runs filters.
*/ */
typedef struct chiseldir_info typedef struct chiseldir_info
{ {

View File

@ -171,7 +171,7 @@ uint32_t lua_cbacks::rawval_to_lua_stack(lua_State *ls, uint8_t* rawval, ppm_par
strcpy(address, "<NA>"); strcpy(address, "<NA>");
} }
strncpy(ch->m_lua_fld_storage, strlcpy(ch->m_lua_fld_storage,
address, address,
sizeof(ch->m_lua_fld_storage)); sizeof(ch->m_lua_fld_storage));

View File

@ -1,7 +1,6 @@
/* /*
Copyright (C) 2021 The Falco Authors. Copyright (C) 2021 The Falco Authors.
This file is part of sysdig.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@ -16,9 +16,8 @@ limitations under the License.
*/ */
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include "sinsp.h" #include <memory>
#include "filter.h" #include "gen_filter.h"
#include "sinsp_int.h"
#include "lua_parser.h" #include "lua_parser.h"
#include "lua_parser_api.h" #include "lua_parser_api.h"
@ -30,7 +29,7 @@ extern "C" {
#include "lauxlib.h" #include "lauxlib.h"
} }
const static struct luaL_reg ll_filter [] = const static struct luaL_Reg ll_filter [] =
{ {
{"rel_expr", &lua_parser_cbacks::rel_expr}, {"rel_expr", &lua_parser_cbacks::rel_expr},
{"bool_op", &lua_parser_cbacks::bool_op}, {"bool_op", &lua_parser_cbacks::bool_op},
@ -39,50 +38,29 @@ const static struct luaL_reg ll_filter [] =
{NULL,NULL} {NULL,NULL}
}; };
lua_parser::lua_parser(gen_event_filter_factory &factory, lua_State *ls, const char *lua_library_name) lua_parser::lua_parser(std::shared_ptr<gen_event_filter_factory> factory)
: m_factory(factory) : m_factory(factory), m_filter(m_factory->new_filter()),
m_last_boolop(BO_NONE), m_have_rel_expr(false),
m_nest_level(0)
{ {
m_filter = NULL;
m_ls = ls;
reset();
// Register our c++ defined functions
luaL_openlib(m_ls, lua_library_name, ll_filter, 0);
} }
void lua_parser::reset()
{
m_have_rel_expr = false;
m_last_boolop = BO_NONE;
m_nest_level = 0;
m_filter = m_factory.new_filter();
}
gen_event_filter* lua_parser::get_filter(bool reset_filter)
{
if (m_nest_level != 0)
{
throw sinsp_exception("Error in configured filter: unbalanced nesting");
}
gen_event_filter *ret = m_filter;
if (reset_filter)
{
reset();
}
return ret;
}
lua_parser::~lua_parser() lua_parser::~lua_parser()
{ {
// The lua state is not considered owned by this object, so
// not freeing it.
delete m_filter;
m_filter = NULL;
} }
void lua_parser::register_callbacks(lua_State *ls, const char *lua_library_name)
{
// Register our c++ defined functions
luaL_openlib(ls, lua_library_name, ll_filter, 0);
}
std::shared_ptr<gen_event_filter> lua_parser::filter()
{
return m_filter;
}
std::shared_ptr<gen_event_filter_factory> lua_parser::factory()
{
return m_factory;
}

View File

@ -24,23 +24,22 @@ typedef struct lua_State lua_State;
class lua_parser class lua_parser
{ {
public: public:
lua_parser(gen_event_filter_factory &factory, lua_State *ls, const char *lua_library_name); lua_parser(std::shared_ptr<gen_event_filter_factory> factory);
virtual ~lua_parser(); virtual ~lua_parser();
gen_event_filter* get_filter(bool reset_filter = false);
std::shared_ptr<gen_event_filter> filter();
std::shared_ptr<gen_event_filter_factory> factory();
static void register_callbacks(lua_State *ls, const char *lua_library_name);
private: private:
std::shared_ptr<gen_event_filter_factory> m_factory;
void reset(); std::shared_ptr<gen_event_filter> m_filter;
gen_event_filter_factory &m_factory;
gen_event_filter* m_filter;
boolop m_last_boolop; boolop m_last_boolop;
bool m_have_rel_expr; bool m_have_rel_expr;
int32_t m_nest_level; int32_t m_nest_level;
lua_State* m_ls;
friend class lua_parser_cbacks; friend class lua_parser_cbacks;
}; };

View File

@ -14,6 +14,9 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
#include <memory>
#include "sinsp.h" #include "sinsp.h"
#include "sinsp_int.h" #include "sinsp_int.h"
@ -131,9 +134,7 @@ int lua_parser_cbacks::nest(lua_State *ls)
throw sinsp_exception(err); throw sinsp_exception(err);
} }
gen_event_filter* filter = parser->m_filter; parser->filter()->push_expression(parser->m_last_boolop);
filter->push_expression(parser->m_last_boolop);
parser->m_nest_level++; parser->m_nest_level++;
parser->m_last_boolop = BO_NONE; parser->m_last_boolop = BO_NONE;
@ -142,10 +143,11 @@ int lua_parser_cbacks::nest(lua_State *ls)
catch (const std::exception& e) catch (const std::exception& e)
{ {
lua_pushstring(ls, e.what()); lua_pushstring(ls, e.what());
lua_error(ls); return 1;
} }
return 0; lua_pushnil(ls);
return 1;
} }
int lua_parser_cbacks::unnest(lua_State *ls) int lua_parser_cbacks::unnest(lua_State *ls)
@ -160,18 +162,17 @@ int lua_parser_cbacks::unnest(lua_State *ls)
throw sinsp_exception(err); throw sinsp_exception(err);
} }
gen_event_filter* filter = parser->m_filter; parser->filter()->pop_expression();
filter->pop_expression();
parser->m_nest_level--; parser->m_nest_level--;
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
lua_pushstring(ls, e.what()); lua_pushstring(ls, e.what());
lua_error(ls); return 1;
} }
return 0; lua_pushnil(ls);
return 1;
} }
int lua_parser_cbacks::bool_op(lua_State *ls) int lua_parser_cbacks::bool_op(lua_State *ls)
@ -212,9 +213,11 @@ int lua_parser_cbacks::bool_op(lua_State *ls)
catch (const std::exception& e) catch (const std::exception& e)
{ {
lua_pushstring(ls, e.what()); lua_pushstring(ls, e.what());
lua_error(ls); return 1;
} }
return 0;
lua_pushnil(ls);
return 1;
} }
@ -231,10 +234,9 @@ int lua_parser_cbacks::rel_expr(lua_State *ls)
} }
parser->m_have_rel_expr = true; parser->m_have_rel_expr = true;
gen_event_filter* filter = parser->m_filter;
const char* fld = luaL_checkstring(ls, 2); const char* fld = luaL_checkstring(ls, 2);
gen_event_filter_check *chk = parser->m_factory.new_filtercheck(fld); gen_event_filter_check *chk = parser->factory()->new_filtercheck(fld);
if(chk == NULL) if(chk == NULL)
{ {
string err = "filter_check called with nonexistent field " + string(fld); string err = "filter_check called with nonexistent field " + string(fld);
@ -264,7 +266,7 @@ int lua_parser_cbacks::rel_expr(lua_State *ls)
string err = "Got non-table as in-expression operand for field " + string(fld); string err = "Got non-table as in-expression operand for field " + string(fld);
throw sinsp_exception(err); throw sinsp_exception(err);
} }
int n = luaL_getn(ls, 4); /* get size of table */ int n = lua_objlen(ls, 4); /* get size of table */
for (i=1; i<=n; i++) for (i=1; i<=n; i++)
{ {
lua_rawgeti(ls, 4, i); lua_rawgeti(ls, 4, i);
@ -297,15 +299,16 @@ int lua_parser_cbacks::rel_expr(lua_State *ls)
chk->set_check_id(rule_index); chk->set_check_id(rule_index);
} }
filter->add_check(chk); parser->filter()->add_check(chk);
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
lua_pushstring(ls, e.what()); lua_pushstring(ls, e.what());
lua_error(ls); return 1;
} }
return 0; lua_pushnil(ls);
return 1;
} }

View File

@ -0,0 +1,46 @@
/*
Copyright (C) 2021 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <sys/types.h>
#include <string.h>
#pragma once
/*!
\brief Copy up to size - 1 characters from the NUL-terminated string src to dst, NUL-terminating the result.
\return The length of the source string.
*/
#ifndef HAVE_STRLCPY
static inline size_t strlcpy(char *dst, const char *src, size_t size) {
size_t srcsize = strlen(src);
if (size == 0) {
return srcsize;
}
size_t copysize = srcsize;
if (copysize > size - 1) {
copysize = size - 1;
}
memcpy(dst, src, copysize);
dst[copysize] = '\0';
return srcsize;
}
#endif

View File

@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
include_directories(../../common) include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../../common")
option(USE_BUNDLED_DEPS "Enable bundled dependencies instead of using the system ones" ON) option(USE_BUNDLED_DEPS "Enable bundled dependencies instead of using the system ones" ON)
@ -27,20 +27,40 @@ add_definitions(-DPLATFORM_NAME="${CMAKE_SYSTEM_NAME}")
if(CMAKE_SYSTEM_NAME MATCHES "Linux") if(CMAKE_SYSTEM_NAME MATCHES "Linux")
if(CMAKE_BUILD_TYPE STREQUAL "Debug") if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(KBUILD_FLAGS "${SYSDIG_DEBUG_FLAGS}") set(KBUILD_FLAGS "${FALCOSECURITY_LIBS_DEBUG_FLAGS}")
endif() endif()
if(NOT DEFINED PROBE_VERSION) if(NOT DEFINED PROBE_VERSION)
set(PROBE_VERSION "${SYSDIG_VERSION}") set(PROBE_VERSION "${FALCOSECURITY_LIBS_VERSION}")
endif() endif()
if(NOT DEFINED PROBE_NAME) if(NOT DEFINED PROBE_NAME)
set(PROBE_NAME "sysdig-probe") set(PROBE_NAME "scap")
endif() endif()
if(NOT DEFINED PROBE_DEVICE_NAME) if(NOT DEFINED PROBE_DEVICE_NAME)
set(PROBE_DEVICE_NAME "sysdig") set(PROBE_DEVICE_NAME "${PROBE_NAME}")
endif() endif()
string(REPLACE "-" "_" SCAP_PROBE_MODULE_NAME "${PROBE_NAME}")
add_definitions(-DSCAP_PROBE_MODULE_NAME="${SCAP_PROBE_MODULE_NAME}")
if(NOT DEFINED SCAP_PROBE_BPF_FILEPATH)
# note that the home folder is prepended by scap at runtime
set(SCAP_PROBE_BPF_FILEPATH ".${PROBE_NAME}/${PROBE_NAME}-bpf.o")
endif() endif()
add_definitions(-DSCAP_PROBE_BPF_FILEPATH="${SCAP_PROBE_BPF_FILEPATH}")
endif()
if(NOT DEFINED SCAP_BPF_PROBE_ENV_VAR_NAME)
set(SCAP_BPF_PROBE_ENV_VAR_NAME "BPF_PROBE")
endif()
add_definitions(-DSCAP_BPF_PROBE_ENV_VAR_NAME="${SCAP_BPF_PROBE_ENV_VAR_NAME}")
if(NOT DEFINED SCAP_HOST_ROOT_ENV_VAR_NAME)
set(SCAP_HOST_ROOT_ENV_VAR_NAME "HOST_ROOT")
endif()
add_definitions(-DSCAP_HOST_ROOT_ENV_VAR_NAME="${SCAP_HOST_ROOT_ENV_VAR_NAME}")
if(CYGWIN) if(CYGWIN)
include_directories("${WIN_HAL_INCLUDE}") include_directories("${WIN_HAL_INCLUDE}")

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
--- ---
layout: default layout: default
title: sysdig | libscap title: falcosecurity | libscap
--- ---
<link href="{{ site.baseurl }}assets/vendor/doxygen/tabs.css" rel="stylesheet" type="text/css"/> <link href="{{ site.baseurl }}assets/vendor/doxygen/tabs.css" rel="stylesheet" type="text/css"/>

View File

@ -0,0 +1,581 @@
/*
Copyright (C) 2021 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include <stdbool.h>
#include <inttypes.h>
//
// This file contains the prototype and type definitions of sinsp/scap plugins
//
//
// API versions of this plugin engine
//
#define PLUGIN_API_VERSION_MAJOR 0
#define PLUGIN_API_VERSION_MINOR 3
#define PLUGIN_API_VERSION_PATCH 0
//
// Just some not so smart defines to retrieve plugin api version as string
//
#define QUOTE(str) #str
#define EXPAND_AND_QUOTE(str) QUOTE(str)
#define PLUGIN_API_VERSION PLUGIN_API_VERSION_MAJOR.PLUGIN_API_VERSION_MINOR.PLUGIN_API_VERSION_PATCH
#define PLUGIN_API_VERSION_STR EXPAND_AND_QUOTE(PLUGIN_API_VERSION)
//
// There are two plugin types: source plugins and extractor plugins.
//
// Source plugins implement a new sinsp/scap event source and have the
// ability to provide events to the event loop. Optionally, they can
// extract fields from events so they can be displayed/used in
// filters.
//
// Extractor plugins do not provide events, but have the ability to
// extract fields from events created by other plugins. A good example
// of an extractor plugin is a json extractor, which can extract
// information from any json payload, regardless of where the payloads
// come from.
//
typedef enum ss_plugin_type
{
TYPE_SOURCE_PLUGIN = 1,
TYPE_EXTRACTOR_PLUGIN = 2
}ss_plugin_type;
// The noncontinguous numbers are to maintain equality with underlying
// falcosecurity libs types.
typedef enum ss_plugin_field_type
{
FTYPE_UINT64 = 8,
FTYPE_STRING = 9
}ss_plugin_field_type;
// Values to return from init() / open() / next_batch() /
// extract_fields().
typedef enum ss_plugin_rc
{
SS_PLUGIN_SUCCESS = 0,
SS_PLUGIN_FAILURE = 1,
SS_PLUGIN_TIMEOUT = -1,
SS_PLUGIN_EOF = 2,
SS_PLUGIN_NOT_SUPPORTED = 3,
} ss_plugin_rc;
// The supported schema formats for the init configuration.
typedef enum ss_plugin_schema_type
{
// The schema is undefined and the init configuration
// is an opaque string.
SS_PLUGIN_SCHEMA_NONE = 0,
//
// The schema follows the JSON Schema specific, and the
// init configuration must be represented as a json.
// see: https://json-schema.org/
SS_PLUGIN_SCHEMA_JSON = 1,
} ss_plugin_schema_type;
// This struct represents an event returned by the plugin, and is used
// below in next_batch().
// - evtnum: incremented for each event returned. Might not be contiguous.
// - data: pointer to a memory buffer pointer. The plugin will set it
// to point to the memory containing the next event.
// - datalen: pointer to a 32bit integer. The plugin will set it the size of the
// buffer pointed by data.
// - ts: the event timestamp, in nanoseconds since the epoch.
// Can be (uint64_t)-1, in which case the engine will automatically
// fill the event time with the current time.
//
// Note: event numbers are assigned by the plugin
// framework. Therefore, there isn't any need to fill in evtnum when
// returning an event via plugin_next_batch. It will be ignored.
typedef struct ss_plugin_event
{
uint64_t evtnum;
const uint8_t *data;
uint32_t datalen;
uint64_t ts;
} ss_plugin_event;
// Used in extract_fields functions below to receive a field/arg
// pair and return an extracted value.
// field_id: id of the field, as of its index in the list of
// fields specified by the plugin.
// field: the field name.
// arg: the field argument, if an argument has been specified
// for the field, otherwise it's NULL.
// For example:
// * if the field specified by the user is foo.bar[pippo], arg will be the
// string "pippo"
// * if the field specified by the user is foo.bar, arg will be NULL
// ftype: the type of the field. Could be derived from the field name alone,
// but including here can prevent a second lookup of field names.
// The following should be filled in by the extraction function:
// - field_present: set to true if the event has a meaningful
// extracted value for the provided field, false otherwise
// - res_str: if the corresponding field was type==string, this should be
// filled in with the string value. The string must be allocated and set
// by the plugin.
// - res_u64: if the corresponding field was type==uint64, this should be
// filled in with the uint64 value.
typedef struct ss_plugin_extract_field
{
uint32_t field_id;
const char* field;
const char* arg;
uint32_t ftype;
bool field_present;
const char* res_str;
uint64_t res_u64;
} ss_plugin_extract_field;
//
// This is the opaque pointer to the state of a plugin.
// It points to any data that might be needed plugin-wise. It is
// allocated by init() and must be destroyed by destroy().
// It is defined as void because the engine doesn't care what it is
// and it treats is as opaque.
//
typedef void ss_plugin_t;
//
// This is the opaque pointer to the state of an open instance of the source
// plugin.
// It points to any data that is needed while a capture is running. It is
// allocated by open() and must be destroyed by close().
// It is defined as void because the engine doesn't care what it is
// and it treats is as opaque.
//
typedef void ss_instance_t;
//
// The structs below define the functions and arguments for source and
// extractor plugins. The structs are used by the plugin framework to
// load and interface with plugins.
//
// From the perspective of the plugin, each function below should be
// exported from the dynamic library as a C calling convention
// function, adding a prefix "plugin_" to the function name
// (e.g. plugin_get_required_api_version, plugin_init, etc.)
//
// Plugins are totally responsible of both allocating and deallocating memory.
// Plugins have the guarantee that they can safely deallocate memory in
// these cases:
// - During close(), for all the memory allocated in the context of a plugin
// instance after open().
// - During destroy(), for all the memory allocated by the plugin, as it stops
// being executed.
// - During subsequent calls to the same function, for all the exported
// functions returning memory pointers.
//
// Plugins must not free memory passed in by the framework (i.e. function input
// parameters) if not corresponding to plugin-allocated memory in the
// cases above. Plugins can safely use the passed memory during the execution
// of the exported functions.
//
// Interface for a sinsp/scap source plugin.
//
typedef struct
{
//
// Return the version of the plugin API used by this plugin.
// Required: yes
// Return value: the API version string, in the following format:
// "<major>.<minor>.<patch>", e.g. "1.2.3".
// NOTE: to ensure correct interoperability between the engine and the plugins,
// we use a semver approach. Plugins are required to specify the version
// of the API they run against, and the engine will take care of checking
// and enforcing compatibility.
//
const char* (*get_required_api_version)();
//
// Return the plugin type.
// Required: yes
// Should return TYPE_SOURCE_PLUGIN. It still makes sense to
// have a function get_type() as the plugin interface will
// often dlsym() functions from shared libraries, and can't
// inspect any C struct type.
//
uint32_t (*get_type)();
//
// Return a string representation of a schema describing the data expected
// to be passed as a configuration during the plugin initialization.
// Required: no
// Arguments:
// - schema_type: The schema format type of the returned value among the
// list of the supported ones according to the ss_plugin_config_schema
// enumeration.
// Return value: a string representation of the schema for the config
// to be passed to init().
//
// Plugins can optionally export this symbol to specify the expected
// format for the configuration string passed to init(). If specified,
// the init() function can assume the config string to always be
// well-formed. The framework will take care of automatically parsing it
// against the provided schema and generating ad-hoc errors accordingly.
// This also serves as a piece of documentation for users about how the
// plugin needs to be configured.
//
const char* (*get_init_schema)(ss_plugin_schema_type* schema_type);
//
// Initialize the plugin and, if needed, allocate its state.
// Required: yes
// Arguments:
// - config: a string with the plugin configuration. The format of the
// string is chosen by the plugin itself.
// - rc: pointer to a ss_plugin_rc that will contain the initialization result
// Return value: pointer to the plugin state that will be treated as opaque
// by the engine and passed to the other plugin functions.
// If rc is SS_PLUGIN_FAILURE, this function should return NULL.
//
ss_plugin_t* (*init)(const char* config, ss_plugin_rc* rc);
//
// Destroy the plugin and, if plugin state was allocated, free it.
// Required: yes
//
void (*destroy)(ss_plugin_t* s);
//
// Return a string with the error that was last generated by
// the plugin.
// Required: yes
//
// In cases where any other api function returns an error, the
// plugin should be prepared to return a human-readable error
// string with more context for the error. The plugin manager
// calls get_last_error() to access that string.
//
const char* (*get_last_error)(ss_plugin_t* s);
//
// Return the unique ID of the plugin.
// Required: yes
// EVERY SOURCE PLUGIN (see get_type()) MUST OBTAIN AN OFFICIAL ID FROM THE
// FALCOSECURITY ORGANIZATION, OTHERWISE IT WON'T PROPERLY COEXIST WITH OTHER PLUGINS.
//
uint32_t (*get_id)();
//
// Return the name of the plugin, which will be printed when displaying
// information about the plugin.
// Required: yes
//
const char* (*get_name)();
//
// Return the descriptions of the plugin, which will be printed when displaying
// information about the plugin or its events.
// Required: yes
//
const char* (*get_description)();
//
// Return a string containing contact info (url, email, twitter, etc) for
// the plugin authors.
// Required: yes
//
const char* (*get_contact)();
//
// Return the version of this plugin itself
// Required: yes
// Return value: a string with a version identifier, in the following format:
// "<major>.<minor>.<patch>", e.g. "1.2.3".
// This differs from the api version in that this versions the
// plugin itself, as compared to the plugin interface. When
// reading capture files, the major version of the plugin that
// generated events must match the major version of the plugin
// used to read events.
//
const char* (*get_version)();
//
// Return a string describing the events generated by this source plugin.
// Required: yes
// Example event sources would be strings like "syscall",
// "k8s_audit", etc. The source can be used by extractor
// plugins to filter the events they receive.
//
const char* (*get_event_source)();
//
// Return the list of extractor fields exported by this plugin. Extractor
// fields can be used in Falco rule conditions and sysdig filters.
// Required: no
// Return value: a string with the list of fields encoded as a json
// array.
// Each field entry is a json object with the following properties:
// "type": one of "string", "uint64"
// "name": a string with a name for the field
// "argRequired: (optional) If present and set to true, notes
// that the field requires an argument e.g. field[arg].
// "display": (optional) If present, a string that will be used to
// display the field instead of the name. Used in tools
// like wireshark.
// "desc": a string with a description of the field
// Example return value:
// [
// {"type": "string", "name": "field1", "argRequired": true, "desc": "Describing field 1"},
// {"type": "uint64", "name": "field2", "desc": "Describing field 2"}
// ]
const char* (*get_fields)();
//
// Open the source and start a capture (e.g. stream of events)
// Required: yes
// Arguments:
// - s: the plugin state returned by init()
// - params: the open parameters, as a string. The format is defined by the plugin
// itsef
// - rc: pointer to a ss_plugin_rc that will contain the open result
// Return value: a pointer to the open context that will be passed to next_batch(),
// close(), event_to_string() and extract_fields.
//
ss_instance_t* (*open)(ss_plugin_t* s, const char* params, ss_plugin_rc* rc);
//
// Close a capture.
// Required: yes
// Arguments:
// - s: the plugin context, returned by init(). Can be NULL.
// - h: the capture context, returned by open(). Can be NULL.
//
void (*close)(ss_plugin_t* s, ss_instance_t* h);
//
// Return a list of suggested open parameters supported by this plugin.
// Any of the values in the returned list are valid parameters for open().
// Required: no
// Return value: a string with the list of open params encoded as
// a json array.
// Each field entry is a json object with the following properties:
// "value": a string usable as an open() parameter.
// "desc": (optional) a string with a description of the parameter.
// Example return value:
// [
// {"value": "resource1", "desc": "An example of openable resource"},
// {"value": "resource2", "desc": "Another example of openable resource"}
// ]
const char* (*list_open_params)(ss_plugin_t* s, ss_plugin_rc* rc);
//
// Return the read progress.
// Required: no
// Arguments:
// - progress_pct: the read progress, as a number between 0 (no data has been read)
// and 10000 (100% of the data has been read). This encoding allows the engine to
// print progress decimals without requiring to deal with floating point numbers
// (which could cause incompatibility problems with some languages).
// Return value: a string representation of the read
// progress. This might include the progress percentage
// combined with additional context added by the plugin. If
// NULL, progress_pct should be used.
// The returned memory pointer must be allocated by the plugin
// and must not be deallocated or modified until the next call to
// get_progress().
// NOTE: reporting progress is optional and in some case could be impossible. However,
// when possible, it's recommended as it provides valuable information to the
// user.
//
const char* (*get_progress)(ss_plugin_t* s, ss_instance_t* h, uint32_t* progress_pct);
//
// Return a text representation of an event generated by this source plugin.
// Required: yes
// Arguments:
// - data: the buffer from an event produced by next_batch().
// - datalen: the length of the buffer from an event produced by next_batch().
// Return value: the text representation of the event. This is used, for example,
// by sysdig to print a line for the given event.
// The returned memory pointer must be allocated by the plugin
// and must not be deallocated or modified until the next call to
// event_to_string().
//
const char* (*event_to_string)(ss_plugin_t *s, const uint8_t *data, uint32_t datalen);
//
// Extract one or more a filter field values from an event.
// Required: no
// Arguments:
// - evt: an event struct produced by a call to next_batch().
// This is allocated by the framework, and it is not guaranteed
// that the event struct pointer is the same returned by the last
// next_batch() call.
// - num_fields: the length of the fields array.
// - fields: an array of ss_plugin_extract_field structs. Each entry
// contains a single field + optional argument as input, and the corresponding
// extracted value as output. Memory pointers set as output must be allocated
// by the plugin and must not be deallocated or modified until the next
// extract_fields() call.
//
// Return value: A ss_plugin_rc with values SS_PLUGIN_SUCCESS or SS_PLUGIN_FAILURE.
//
ss_plugin_rc (*extract_fields)(ss_plugin_t *s, const ss_plugin_event *evt, uint32_t num_fields, ss_plugin_extract_field *fields);
//
// Return the next batch of events.
// On success:
// - nevts will be filled in with the number of events.
// - evts: pointer to an ss_plugin_event pointer. The plugin must
// allocate an array of contiguous ss_plugin_event structs
// and each data buffer within each ss_plugin_event struct.
// Memory pointers set as output must be allocated by the plugin
// and must not be deallocated or modified until the next call to
// next_batch() or close().
// Required: yes
//
ss_plugin_rc (*next_batch)(ss_plugin_t* s, ss_instance_t* h, uint32_t *nevts, ss_plugin_event **evts);
//
// The following members are PRIVATE for the engine and should not be touched.
//
ss_plugin_t* state;
ss_instance_t* handle;
uint32_t id;
const char* name;
} source_plugin_info;
//
// Interface for a sinsp/scap extractor plugin
//
typedef struct
{
//
// Return the version of the plugin API used by this plugin.
// Required: yes
// Return value: the API version string, in the following format:
// "<major>.<minor>.<patch>", e.g. "1.2.3".
// NOTE: to ensure correct interoperability between the engine and the plugins,
// we use a semver approach. Plugins are required to specify the version
// of the API they run against, and the engine will take care of checking
// and enforcing compatibility.
//
const char* (*get_required_api_version)();
//
// Return the plugin type.
// Required: yes
// Should return TYPE_EXTRACTOR_PLUGIN. It still makes sense to
// have a function get_type() as the plugin interface will
// often dlsym() functions from shared libraries, and can't
// inspect any C struct type.
//
uint32_t (*get_type)();
//
// Return a string representation of a schema describing the data expected
// to be passed as a configuration during the plugin initialization.
// Required: no
// Arguments:
// - schema_type: The schema format type of the returned value among the
// list of the supported ones according to the ss_plugin_config_schema
// enumeration.
// Return value: a string representation of the schema for the config
// to be passed to init().
//
// Plugins can optionally export this symbol to specify the expected
// format for the configuration string passed to init(). If specified,
// the init() function can assume the config string to always be
// well-formed. The framework will take care of automatically parsing it
// against the provided schema and generating ad-hoc errors accordingly.
// This also serves as a piece of documentation for users about how the
// plugin needs to be configured.
//
const char* (*get_init_schema)(ss_plugin_schema_type* schema_type);
//
// Initialize the plugin and, if needed, allocate its state.
// Required: yes
// Arguments:
// - config: a string with the plugin configuration. The format of the
// string is chosen by the plugin itself.
// - rc: pointer to a ss_plugin_rc that will contain the initialization result
// Return value: pointer to the plugin state that will be treated as opaque
// by the engine and passed to the other plugin functions.
//
ss_plugin_t* (*init)(const char* config, ss_plugin_rc* rc);
//
// Destroy the plugin and, if plugin state was allocated, free it.
// Required: yes
//
void (*destroy)(ss_plugin_t* s);
//
// Return a string with the error that was last generated by
// the plugin.
// Required: yes
//
// In cases where any other api function returns an error, the
// plugin should be prepared to return a human-readable error
// string with more context for the error. The plugin manager
// calls get_last_error() to access that string.
//
const char* (*get_last_error)(ss_plugin_t* s);
//
// Return the name of the plugin, which will be printed when displaying
// information about the plugin.
// Required: yes
//
const char* (*get_name)();
//
// Return the descriptions of the plugin, which will be printed when displaying
// information about the plugin or its events.
// Required: yes
//
const char* (*get_description)();
//
// Return a string containing contact info (url, email, twitter, etc) for
// the plugin author.
// Required: yes
//
const char* (*get_contact)();
//
// Return the version of this plugin itself
// Required: yes
// Return value: a string with a version identifier, in the following format:
// "<major>.<minor>.<patch>", e.g. "1.2.3".
// This differs from the api version in that this versions the
// plugin itself, as compared to the plugin interface. When
// reading capture files, the major version of the plugin that
// generated events must match the major version of the plugin
// used to read events.
//
const char* (*get_version)();
//
// Return a string describing the event sources that this
// extractor plugin can consume.
// Required: no
// Return value: a json array of strings containing event
// sources returned by a source plugin's get_event_source()
// function.
// This function is optional--if NULL then the exctractor
// plugin will receive every event.
//
const char* (*get_extract_event_sources)();
//
// Return the list of extractor fields exported by this plugin. Extractor
// fields can be used in Falco rules and sysdig filters.
// Required: yes
// Return value: a string with the list of fields encoded as a json
// array.
//
const char* (*get_fields)();
//
// Extract one or more a filter field values from an event.
// Required: yes
// Arguments:
// - evt: an event struct provided by the framework.
// - num_fields: the length of the fields array.
// - fields: an array of ss_plugin_extract_field structs. Each entry
// contains a single field + optional argument as input, and the corresponding
// extracted value as output. Memory pointers set as output must be allocated
// by the plugin and must not be deallocated or modified until the next
// extract_fields() call.
//
// Return value: A ss_plugin_rc with values SS_PLUGIN_SUCCESS or SS_PLUGIN_FAILURE.
//
ss_plugin_rc (*extract_fields)(ss_plugin_t *s, const ss_plugin_event *evt, uint32_t num_fields, ss_plugin_extract_field *fields);
//
// The following members are PRIVATE for the engine and should not be touched.
//
ss_plugin_t* state;
} extractor_plugin_info;

View File

@ -20,6 +20,7 @@ limitations under the License.
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
#include "settings.h" #include "settings.h"
#include "plugin_info.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -109,6 +110,17 @@ typedef struct scap_tid
UT_hash_handle hh; ///< makes this structure hashable UT_hash_handle hh; ///< makes this structure hashable
} scap_tid; } scap_tid;
typedef enum ppm_reader_type
{
RT_FILE = 0
} ppm_reader_type;
struct scap_reader
{
ppm_reader_type m_type;
gzFile m_file;
};
// //
// The open instance handle // The open instance handle
// //
@ -117,12 +129,10 @@ struct scap
scap_mode_t m_mode; scap_mode_t m_mode;
scap_device* m_devs; scap_device* m_devs;
uint32_t m_ndevs; uint32_t m_ndevs;
#ifdef USE_ZLIB scap_reader_t* m_reader;
gzFile m_file; char* m_reader_evt_buf;
#else size_t m_reader_evt_buf_size;
FILE* m_file;
#endif
char* m_file_evt_buf;
uint32_t m_last_evt_dump_flags; uint32_t m_last_evt_dump_flags;
char m_lasterr[SCAP_LASTERR_SIZE]; char m_lasterr[SCAP_LASTERR_SIZE];
@ -174,6 +184,30 @@ struct scap
// The number of events that were skipped due to the comm // The number of events that were skipped due to the comm
// matching an entry in m_suppressed_comms. // matching an entry in m_suppressed_comms.
uint64_t m_num_suppressed_evts; uint64_t m_num_suppressed_evts;
bool syscalls_of_interest[SYSCALL_TABLE_SIZE];
//
// Plugin-related state
//
source_plugin_info* m_input_plugin;
uint8_t* m_input_plugin_evt_storage;
uint32_t m_input_plugin_evt_storage_len;
// The number of items held in batch_evts
uint32_t m_input_plugin_batch_nevts;
// A set of events returned from next_batch. The array is
// allocated and must be free()d when done.
ss_plugin_event* m_input_plugin_batch_evts;
// The current position into the above arrays (0-indexed),
// reflecting how many of the above items have been returned
// via a call to next().
uint32_t m_input_plugin_batch_idx;
// The return value from the last call to next_batch().
ss_plugin_rc m_input_plugin_last_batch_res;
}; };
typedef enum ppm_dumper_type typedef enum ppm_dumper_type
@ -202,7 +236,7 @@ struct scap_ns_socket_list
// Misc stuff // Misc stuff
// //
#define MEMBER_SIZE(type, member) sizeof(((type *)0)->member) #define MEMBER_SIZE(type, member) sizeof(((type *)0)->member)
#define FILE_READ_BUF_SIZE 65536 #define READER_BUF_SIZE (1 << 16) // UINT16_MAX + 1, ie: 65536
// //
// Internal library functions // Internal library functions
@ -247,9 +281,9 @@ uint32_t scap_fd_info_len(scap_fdinfo* fdi);
// Write the given fd info to disk // Write the given fd info to disk
int32_t scap_fd_write_to_disk(scap_t* handle, scap_fdinfo* fdi, scap_dumper_t* dumper, uint32_t len); int32_t scap_fd_write_to_disk(scap_t* handle, scap_fdinfo* fdi, scap_dumper_t* dumper, uint32_t len);
// Populate the given fd by reading the info from disk // Populate the given fd by reading the info from disk
uint32_t scap_fd_read_from_disk(scap_t* handle, OUT scap_fdinfo* fdi, OUT size_t* nbytes, uint32_t block_type, gzFile f); uint32_t scap_fd_read_from_disk(scap_t* handle, OUT scap_fdinfo* fdi, OUT size_t* nbytes, uint32_t block_type, scap_reader_t* r);
// Parse the headers of a trace file and load the tables // Parse the headers of a trace file and load the tables
int32_t scap_read_init(scap_t* handle, gzFile f); int32_t scap_read_init(scap_t* handle, scap_reader_t* r);
// Add the file descriptor info pointed by fdi to the fd table for process pi. // Add the file descriptor info pointed by fdi to the fd table for process pi.
// Note: silently skips if fdi->type is SCAP_FD_UNKNOWN. // Note: silently skips if fdi->type is SCAP_FD_UNKNOWN.
int32_t scap_add_fd_to_proc_table(scap_t* handle, scap_threadinfo* pi, scap_fdinfo* fdi, char *error); int32_t scap_add_fd_to_proc_table(scap_t* handle, scap_threadinfo* pi, scap_fdinfo* fdi, char *error);
@ -382,6 +416,107 @@ uint32_t udig_set_snaplen(scap_t* handle, uint32_t snaplen);
int32_t udig_stop_dropping_mode(scap_t* handle); int32_t udig_stop_dropping_mode(scap_t* handle);
int32_t udig_start_dropping_mode(scap_t* handle, uint32_t sampling_ratio); int32_t udig_start_dropping_mode(scap_t* handle, uint32_t sampling_ratio);
//
// scap_reader functions implementation
//
static inline scap_reader_t *scap_reader_open_gzfile(gzFile file)
{
if (file == NULL)
{
return NULL;
}
scap_reader_t* r = (scap_reader_t *) malloc (sizeof (scap_reader_t));
r->m_type = RT_FILE;
r->m_file = file;
return r;
}
static inline ppm_reader_type scap_reader_type(scap_reader_t *r)
{
ASSERT(r != NULL);
return r->m_type;
}
static inline int scap_reader_read(scap_reader_t *r, void* buf, uint32_t len)
{
ASSERT(r != NULL);
switch (r->m_type)
{
case RT_FILE:
return gzread(r->m_file, buf, len);
default:
ASSERT(false);
return 0;
}
}
static inline int64_t scap_reader_offset(scap_reader_t *r)
{
ASSERT(r != NULL);
switch (r->m_type)
{
case RT_FILE:
return gzoffset(r->m_file);
default:
ASSERT(false);
return -1;
}
}
static inline int64_t scap_reader_tell(scap_reader_t *r)
{
ASSERT(r != NULL);
switch (r->m_type)
{
case RT_FILE:
return gztell(r->m_file);
default:
ASSERT(false);
return -1;
}
}
static inline int64_t scap_reader_seek(scap_reader_t *r, int64_t offset, int whence)
{
ASSERT(r != NULL);
switch (r->m_type)
{
case RT_FILE:
return gzseek(r->m_file, offset, whence);
default:
ASSERT(false);
return -1;
}
}
static inline const char *scap_reader_error(scap_reader_t *r, int *errnum)
{
ASSERT(r != NULL);
switch (r->m_type)
{
case RT_FILE:
return gzerror(r->m_file, errnum);
default:
ASSERT(false);
*errnum = -1;
return "unknown scap_reader type";
}
}
static inline int scap_reader_close(scap_reader_t *r)
{
ASSERT(r != NULL);
switch (r->m_type)
{
case RT_FILE:
return gzclose(r->m_file);
default:
ASSERT(false);
return -1;
}
}
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -35,6 +35,7 @@ limitations under the License.
#endif // _WIN32 #endif // _WIN32
#include "scap.h" #include "scap.h"
#include "../common/strlcpy.h"
#ifdef HAS_CAPTURE #ifdef HAS_CAPTURE
#if !defined(_WIN32) && !defined(CYGWING_AGENT) #if !defined(_WIN32) && !defined(CYGWING_AGENT)
#include "driver_config.h" #include "driver_config.h"
@ -55,13 +56,39 @@ limitations under the License.
//#define NDEBUG //#define NDEBUG
#include <assert.h> #include <assert.h>
static const char *SYSDIG_BPF_PROBE_ENV = "SYSDIG_BPF_PROBE";
// //
// Probe version string size // Probe version string size
// //
#define SCAP_PROBE_VERSION_SIZE 32 #define SCAP_PROBE_VERSION_SIZE 32
static int32_t plugin_rc_to_scap_rc(ss_plugin_rc plugin_rc)
{
switch(plugin_rc)
{
case SS_PLUGIN_SUCCESS:
return SCAP_SUCCESS;
break;
case SS_PLUGIN_FAILURE:
return SCAP_FAILURE;
break;
case SS_PLUGIN_TIMEOUT:
return SCAP_TIMEOUT;
break;
case SS_PLUGIN_EOF:
return SCAP_EOF;
break;
case SS_PLUGIN_NOT_SUPPORTED:
return SCAP_NOT_SUPPORTED;
break;
default:
ASSERT(false);
return SCAP_FAILURE;
}
ASSERT(false);
return SCAP_FAILURE;
}
const char* scap_getlasterr(scap_t* handle) const char* scap_getlasterr(scap_t* handle)
{ {
return handle ? handle->m_lasterr : "null scap handle"; return handle ? handle->m_lasterr : "null scap handle";
@ -92,7 +119,8 @@ scap_t* scap_open_live_int(char *error, int32_t *rc,
void* proc_callback_context, void* proc_callback_context,
bool import_users, bool import_users,
const char *bpf_probe, const char *bpf_probe,
const char **suppressed_comms) const char **suppressed_comms,
interesting_ppm_sc_set *ppm_sc_of_interest)
{ {
snprintf(error, SCAP_LASTERR_SIZE, "live capture not supported on %s", PLATFORM_NAME); snprintf(error, SCAP_LASTERR_SIZE, "live capture not supported on %s", PLATFORM_NAME);
*rc = SCAP_NOT_SUPPORTED; *rc = SCAP_NOT_SUPPORTED;
@ -117,7 +145,7 @@ static uint32_t get_max_consumers()
{ {
#ifndef _WIN32 #ifndef _WIN32
uint32_t max; uint32_t max;
FILE *pfile = fopen("/sys/module/" PROBE_DEVICE_NAME "_probe/parameters/max_consumers", "r"); FILE *pfile = fopen("/sys/module/" SCAP_PROBE_MODULE_NAME "/parameters/max_consumers", "r");
if(pfile != NULL) if(pfile != NULL)
{ {
int w = fscanf(pfile, "%"PRIu32, &max); int w = fscanf(pfile, "%"PRIu32, &max);
@ -140,7 +168,8 @@ scap_t* scap_open_live_int(char *error, int32_t *rc,
void* proc_callback_context, void* proc_callback_context,
bool import_users, bool import_users,
const char *bpf_probe, const char *bpf_probe,
const char **suppressed_comms) const char **suppressed_comms,
interesting_ppm_sc_set *ppm_sc_of_interest)
{ {
uint32_t j; uint32_t j;
char filename[SCAP_MAX_PATH_SIZE]; char filename[SCAP_MAX_PATH_SIZE];
@ -168,7 +197,7 @@ scap_t* scap_open_live_int(char *error, int32_t *rc,
// While in theory we could always rely on the scap caller to properly // While in theory we could always rely on the scap caller to properly
// set a BPF probe from the environment variable, it's in practice easier // set a BPF probe from the environment variable, it's in practice easier
// to do one more check here in scap so we don't have to repeat the logic // to do one more check here in scap so we don't have to repeat the logic
// in all the possible users of the libraries (sysdig, csysdig, dragent, ...) // in all the possible users of the libraries
// //
if(!bpf_probe) if(!bpf_probe)
{ {
@ -191,7 +220,7 @@ scap_t* scap_open_live_int(char *error, int32_t *rc,
return NULL; return NULL;
} }
snprintf(buf, sizeof(buf), "%s/.sysdig/%s-bpf.o", home, PROBE_NAME); snprintf(buf, sizeof(buf), "%s/%s", home, SCAP_PROBE_BPF_FILEPATH);
bpf_probe = buf; bpf_probe = buf;
} }
} }
@ -256,6 +285,7 @@ scap_t* scap_open_live_int(char *error, int32_t *rc,
handle->m_machine_info.reserved4 = 0; handle->m_machine_info.reserved4 = 0;
handle->m_driver_procinfo = NULL; handle->m_driver_procinfo = NULL;
handle->m_fd_lookup_limit = 0; handle->m_fd_lookup_limit = 0;
#ifdef CYGWING_AGENT #ifdef CYGWING_AGENT
handle->m_whh = NULL; handle->m_whh = NULL;
handle->m_win_buf_handle = NULL; handle->m_win_buf_handle = NULL;
@ -310,6 +340,37 @@ scap_t* scap_open_live_int(char *error, int32_t *rc,
return NULL; return NULL;
} }
if (ppm_sc_of_interest)
{
for (int i = 0; i < PPM_SC_MAX; i++)
{
// We need to convert from PPM_SC to SYSCALL_NR, using the routing table
for(int syscall_nr = 0; syscall_nr < SYSCALL_TABLE_SIZE; syscall_nr++)
{
// Find the match between the ppm_sc and the syscall_nr
if(g_syscall_code_routing_table[syscall_nr] == i)
{
// UF_NEVER_DROP syscalls must be always traced
if (ppm_sc_of_interest->ppm_sc[i] || g_syscall_table[syscall_nr].flags & UF_NEVER_DROP)
{
handle->syscalls_of_interest[syscall_nr] = true;
}
// DO NOT break as some PPM_SC are used multiple times for different syscalls! (eg: PPM_SC_SETRESUID...)
}
}
}
}
else
{
// fallback to trace all syscalls
for (int i = 0; i < SYSCALL_TABLE_SIZE; i++)
{
handle->syscalls_of_interest[i] = true;
}
}
// //
// Open and initialize all the devices // Open and initialize all the devices
// //
@ -351,7 +412,7 @@ scap_t* scap_open_live_int(char *error, int32_t *rc,
else if(errno == EBUSY) else if(errno == EBUSY)
{ {
uint32_t curr_max_consumers = get_max_consumers(); uint32_t curr_max_consumers = get_max_consumers();
snprintf(error, SCAP_LASTERR_SIZE, "Too many sysdig instances attached to device %s. Current value for /sys/module/" PROBE_DEVICE_NAME "_probe/parameters/max_consumers is '%"PRIu32"'.", filename, curr_max_consumers); snprintf(error, SCAP_LASTERR_SIZE, "Too many consumers attached to device %s. Current value for /sys/module/" SCAP_PROBE_MODULE_NAME "/parameters/max_consumers is '%"PRIu32"'.", filename, curr_max_consumers);
} }
else else
{ {
@ -417,6 +478,22 @@ scap_t* scap_open_live_int(char *error, int32_t *rc,
++j; ++j;
} }
for (int i = 0; i < SYSCALL_TABLE_SIZE; i++)
{
if (!handle->syscalls_of_interest[i])
{
// Kmod driver event_mask check uses event_types instead of syscall nr
enum ppm_event_type enter_ev = g_syscall_table[i].enter_event_type;
enum ppm_event_type exit_ev = g_syscall_table[i].exit_event_type;
// Filter unmapped syscalls (that have a g_syscall_table entry with both enter_ev and exit_ev 0ed)
if (enter_ev != 0 && exit_ev != 0)
{
scap_unset_eventmask(handle, enter_ev);
scap_unset_eventmask(handle, exit_ev);
}
}
}
} }
for(j = 0; j < handle->m_ndevs; ++j) for(j = 0; j < handle->m_ndevs; ++j)
@ -442,7 +519,7 @@ scap_t* scap_open_live_int(char *error, int32_t *rc,
} }
// //
// Now that sysdig has done all its /proc parsing, start the capture // Now that /proc parsing has been done, start the capture
// //
if((*rc = scap_start_capture(handle)) != SCAP_SUCCESS) if((*rc = scap_start_capture(handle)) != SCAP_SUCCESS)
{ {
@ -638,15 +715,16 @@ scap_t* scap_open_udig_int(char *error, int32_t *rc,
// //
error[0] = '\0'; error[0] = '\0';
snprintf(filename, sizeof(filename), "%s/proc", scap_get_host_root()); snprintf(filename, sizeof(filename), "%s/proc", scap_get_host_root());
if((*rc = scap_proc_scan_proc_dir(handle, filename, error)) != SCAP_SUCCESS) char procerr[SCAP_LASTERR_SIZE];
if((*rc = scap_proc_scan_proc_dir(handle, filename, procerr)) != SCAP_SUCCESS)
{ {
scap_close(handle); scap_close(handle);
snprintf(error, SCAP_LASTERR_SIZE, "%s", error); snprintf(error, SCAP_LASTERR_SIZE, "%s", procerr);
return NULL; return NULL;
} }
// //
// Now that sysdig has done all its /proc parsing, start the capture // Now that /proc parsing has been done, start the capture
// //
if(udig_begin_capture(handle, error) != SCAP_SUCCESS) if(udig_begin_capture(handle, error) != SCAP_SUCCESS)
{ {
@ -658,7 +736,7 @@ scap_t* scap_open_udig_int(char *error, int32_t *rc,
} }
#endif // !defined(HAS_CAPTURE) || defined(CYGWING_AGENT) #endif // !defined(HAS_CAPTURE) || defined(CYGWING_AGENT)
scap_t* scap_open_offline_int(gzFile gzfile, scap_t* scap_open_offline_int(scap_reader_t* reader,
char *error, char *error,
int32_t *rc, int32_t *rc,
proc_entry_callback proc_callback, proc_entry_callback proc_callback,
@ -691,7 +769,6 @@ scap_t* scap_open_offline_int(gzFile gzfile,
handle->m_proclist = NULL; handle->m_proclist = NULL;
handle->m_dev_list = NULL; handle->m_dev_list = NULL;
handle->m_evtcnt = 0; handle->m_evtcnt = 0;
handle->m_file = NULL;
handle->m_addrlist = NULL; handle->m_addrlist = NULL;
handle->m_userlist = NULL; handle->m_userlist = NULL;
handle->m_machine_info.num_cpus = (uint32_t)-1; handle->m_machine_info.num_cpus = (uint32_t)-1;
@ -709,16 +786,18 @@ scap_t* scap_open_offline_int(gzFile gzfile,
handle->m_suppressed_comms = NULL; handle->m_suppressed_comms = NULL;
handle->m_suppressed_tids = NULL; handle->m_suppressed_tids = NULL;
handle->m_file_evt_buf = (char*)malloc(FILE_READ_BUF_SIZE); handle->m_reader_evt_buf = (char*)malloc(READER_BUF_SIZE);
if(!handle->m_file_evt_buf) if(!handle->m_reader_evt_buf)
{ {
snprintf(error, SCAP_LASTERR_SIZE, "error allocating the read buffer"); snprintf(error, SCAP_LASTERR_SIZE, "error allocating the read buffer");
scap_close(handle); scap_close(handle);
*rc = SCAP_FAILURE; *rc = SCAP_FAILURE;
return NULL; return NULL;
} }
handle->m_reader_evt_buf_size = READER_BUF_SIZE;
handle->m_file = gzfile; handle->m_reader = reader;
handle->m_unexpected_block_readsize = 0;
// //
// If this is a merged file, we might have to move the read offset to the next section // If this is a merged file, we might have to move the read offset to the next section
@ -731,7 +810,7 @@ scap_t* scap_open_offline_int(gzFile gzfile,
// //
// Validate the file and load the non-event blocks // Validate the file and load the non-event blocks
// //
if((*rc = scap_read_init(handle, handle->m_file)) != SCAP_SUCCESS) if((*rc = scap_read_init(handle, handle->m_reader)) != SCAP_SUCCESS)
{ {
snprintf(error, SCAP_LASTERR_SIZE, "Could not initialize reader: %s", scap_getlasterr(handle)); snprintf(error, SCAP_LASTERR_SIZE, "Could not initialize reader: %s", scap_getlasterr(handle));
scap_close(handle); scap_close(handle);
@ -779,8 +858,9 @@ scap_t* scap_open_offline(const char* fname, char *error, int32_t* rc)
*rc = SCAP_FAILURE; *rc = SCAP_FAILURE;
return NULL; return NULL;
} }
scap_reader_t* reader = scap_reader_open_gzfile(gzfile);
return scap_open_offline_int(gzfile, error, rc, NULL, NULL, true, 0, NULL); return scap_open_offline_int(reader, error, rc, NULL, NULL, true, 0, NULL);
} }
scap_t* scap_open_offline_fd(int fd, char *error, int32_t *rc) scap_t* scap_open_offline_fd(int fd, char *error, int32_t *rc)
@ -792,13 +872,14 @@ scap_t* scap_open_offline_fd(int fd, char *error, int32_t *rc)
*rc = SCAP_FAILURE; *rc = SCAP_FAILURE;
return NULL; return NULL;
} }
scap_reader_t* reader = scap_reader_open_gzfile(gzfile);
return scap_open_offline_int(gzfile, error, rc, NULL, NULL, true, 0, NULL); return scap_open_offline_int(reader, error, rc, NULL, NULL, true, 0, NULL);
} }
scap_t* scap_open_live(char *error, int32_t *rc) scap_t* scap_open_live(char *error, int32_t *rc)
{ {
return scap_open_live_int(error, rc, NULL, NULL, true, NULL, NULL); return scap_open_live_int(error, rc, NULL, NULL, true, NULL, NULL, NULL);
} }
scap_t* scap_open_nodriver_int(char *error, int32_t *rc, scap_t* scap_open_nodriver_int(char *error, int32_t *rc,
@ -918,6 +999,84 @@ scap_t* scap_open_nodriver_int(char *error, int32_t *rc,
#endif // HAS_CAPTURE #endif // HAS_CAPTURE
} }
scap_t* scap_open_plugin_int(char *error, int32_t *rc, source_plugin_info* input_plugin, char* input_plugin_params)
{
scap_t* handle = NULL;
//
// Allocate the handle
//
handle = (scap_t*)malloc(sizeof(scap_t));
if(!handle)
{
snprintf(error, SCAP_LASTERR_SIZE, "error allocating the scap_t structure");
*rc = SCAP_FAILURE;
return NULL;
}
//
// Preliminary initializations
//
memset(handle, 0, sizeof(scap_t));
handle->m_mode = SCAP_MODE_PLUGIN;
//
// Extract machine information
//
handle->m_proc_callback = NULL;
handle->m_proc_callback_context = NULL;
#ifdef _WIN32
handle->m_machine_info.num_cpus = 0;
handle->m_machine_info.memory_size_bytes = 0;
#else
handle->m_machine_info.num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
handle->m_machine_info.memory_size_bytes = (uint64_t)sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE);
#endif
gethostname(handle->m_machine_info.hostname, sizeof(handle->m_machine_info.hostname) / sizeof(handle->m_machine_info.hostname[0]));
handle->m_machine_info.reserved1 = 0;
handle->m_machine_info.reserved2 = 0;
handle->m_machine_info.reserved3 = 0;
handle->m_machine_info.reserved4 = 0;
handle->m_driver_procinfo = NULL;
handle->m_fd_lookup_limit = SCAP_NODRIVER_MAX_FD_LOOKUP; // fd lookup is limited here because is very expensive
handle->m_fake_kernel_proc.tid = -1;
handle->m_fake_kernel_proc.pid = -1;
handle->m_fake_kernel_proc.flags = 0;
snprintf(handle->m_fake_kernel_proc.comm, SCAP_MAX_PATH_SIZE, "kernel");
snprintf(handle->m_fake_kernel_proc.exe, SCAP_MAX_PATH_SIZE, "kernel");
handle->m_fake_kernel_proc.args[0] = 0;
handle->refresh_proc_table_when_saving = true;
handle->m_input_plugin = input_plugin;
handle->m_input_plugin->name = handle->m_input_plugin->get_name();
handle->m_input_plugin->id = handle->m_input_plugin->get_id();
// Set the rc to SCAP_FAILURE now, so in the unlikely event
// that a plugin doesn't not actually set a rc, that it gets
// treated as a failure.
ss_plugin_rc plugin_rc = SCAP_FAILURE;
handle->m_input_plugin->handle = handle->m_input_plugin->open(handle->m_input_plugin->state,
input_plugin_params,
&plugin_rc);
*rc = plugin_rc_to_scap_rc(plugin_rc);
handle->m_input_plugin_batch_nevts = 0;
handle->m_input_plugin_batch_evts = NULL;
handle->m_input_plugin_batch_idx = 0;
handle->m_input_plugin_last_batch_res = SCAP_SUCCESS;
if(*rc != SCAP_SUCCESS)
{
const char *errstr = handle->m_input_plugin->get_last_error(handle->m_input_plugin->state);
snprintf(error, SCAP_LASTERR_SIZE, "%s", errstr);
scap_close(handle);
return NULL;
}
return handle;
}
scap_t* scap_open(scap_open_args args, char *error, int32_t *rc) scap_t* scap_open(scap_open_args args, char *error, int32_t *rc)
{ {
switch(args.mode) switch(args.mode)
@ -949,7 +1108,8 @@ scap_t* scap_open(scap_open_args args, char *error, int32_t *rc)
return NULL; return NULL;
} }
return scap_open_offline_int(gzfile, error, rc, scap_reader_t* reader = scap_reader_open_gzfile(gzfile);
return scap_open_offline_int(reader, error, rc,
args.proc_callback, args.proc_callback_context, args.proc_callback, args.proc_callback_context,
args.import_users, args.start_offset, args.import_users, args.start_offset,
args.suppressed_comms); args.suppressed_comms);
@ -969,10 +1129,11 @@ scap_t* scap_open(scap_open_args args, char *error, int32_t *rc)
args.proc_callback_context, args.proc_callback_context,
args.import_users, args.import_users,
args.bpf_probe, args.bpf_probe,
args.suppressed_comms); args.suppressed_comms,
&args.ppm_sc_of_interest);
} }
#else #else
snprintf(error, SCAP_LASTERR_SIZE, "scap_open: live mode currently not supported on windows. Use nodriver mode instead."); snprintf(error, SCAP_LASTERR_SIZE, "scap_open: live mode currently not supported on Windows.");
*rc = SCAP_NOT_SUPPORTED; *rc = SCAP_NOT_SUPPORTED;
return NULL; return NULL;
#endif #endif
@ -980,6 +1141,8 @@ scap_t* scap_open(scap_open_args args, char *error, int32_t *rc)
return scap_open_nodriver_int(error, rc, args.proc_callback, return scap_open_nodriver_int(error, rc, args.proc_callback,
args.proc_callback_context, args.proc_callback_context,
args.import_users); args.import_users);
case SCAP_MODE_PLUGIN:
return scap_open_plugin_int(error, rc, args.input_plugin, args.input_plugin_params);
case SCAP_MODE_NONE: case SCAP_MODE_NONE:
// error // error
break; break;
@ -1024,17 +1187,84 @@ void scap_close_udig(scap_t* handle)
} }
#endif #endif
static inline void scap_deinit_state(scap_t* handle)
{
// Free the process table
if(handle->m_proclist != NULL)
{
scap_proc_free_table(handle);
handle->m_proclist = NULL;
}
// Free the device table
if(handle->m_dev_list != NULL)
{
scap_free_device_table(handle);
handle->m_dev_list = NULL;
}
// Free the interface list
if(handle->m_addrlist)
{
scap_free_iflist(handle->m_addrlist);
handle->m_addrlist = NULL;
}
// Free the user list
if(handle->m_userlist)
{
scap_free_userlist(handle->m_userlist);
handle->m_userlist = NULL;
}
if(handle->m_driver_procinfo)
{
free(handle->m_driver_procinfo);
handle->m_driver_procinfo = NULL;
}
}
uint32_t scap_restart_capture(scap_t* handle)
{
uint32_t res;
if (handle->m_mode != SCAP_MODE_CAPTURE)
{
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "capture restart supported only in capture mode");
res = SCAP_FAILURE;
}
else
{
scap_deinit_state(handle);
if (handle->m_unexpected_block_readsize > 0)
{
scap_reader_seek(handle->m_reader, (int64_t)0 - (int64_t)handle->m_unexpected_block_readsize, SEEK_CUR);
handle->m_unexpected_block_readsize = 0;
}
if((res = scap_read_init(handle, handle->m_reader)) != SCAP_SUCCESS)
{
char error[SCAP_LASTERR_SIZE];
snprintf(error, SCAP_LASTERR_SIZE, "could not restart capture: %s", scap_getlasterr(handle));
strncpy(handle->m_lasterr, error, SCAP_LASTERR_SIZE);
}
}
return res;
}
void scap_close(scap_t* handle) void scap_close(scap_t* handle)
{ {
if(handle->m_file) if(handle->m_mode == SCAP_MODE_CAPTURE)
{ {
gzclose(handle->m_file); if (handle->m_reader)
{
scap_reader_close(handle->m_reader);
handle->m_reader = NULL;
}
} }
else if(handle->m_mode == SCAP_MODE_LIVE) else if(handle->m_mode == SCAP_MODE_LIVE)
{ {
#if defined(HAS_CAPTURE) && !defined(CYGWING_AGENT) #if defined(HAS_CAPTURE) && !defined(CYGWING_AGENT)
ASSERT(handle->m_file == NULL); ASSERT(handle->m_reader == NULL);
if(handle->m_devs != NULL) if(handle->m_devs != NULL)
{ {
@ -1079,6 +1309,11 @@ void scap_close(scap_t* handle)
} }
#endif // HAS_CAPTURE #endif // HAS_CAPTURE
} }
else if(handle->m_mode == SCAP_MODE_PLUGIN)
{
handle->m_input_plugin->close(handle->m_input_plugin->state, handle->m_input_plugin->handle);
handle->m_input_plugin->handle = NULL;
}
#if CYGWING_AGENT || _WIN32 #if CYGWING_AGENT || _WIN32
if(handle->m_whh != NULL) if(handle->m_whh != NULL)
@ -1087,40 +1322,13 @@ void scap_close(scap_t* handle)
} }
#endif #endif
if(handle->m_file_evt_buf) if(handle->m_reader_evt_buf)
{ {
free(handle->m_file_evt_buf); free(handle->m_reader_evt_buf);
handle->m_reader_evt_buf = NULL;
} }
// Free the process table scap_deinit_state(handle);
if(handle->m_proclist != NULL)
{
scap_proc_free_table(handle);
}
// Free the device table
if(handle->m_dev_list != NULL)
{
scap_free_device_table(handle);
}
// Free the interface list
if(handle->m_addrlist)
{
scap_free_iflist(handle->m_addrlist);
}
// Free the user list
if(handle->m_userlist)
{
scap_free_userlist(handle->m_userlist);
}
if(handle->m_driver_procinfo)
{
free(handle->m_driver_procinfo);
handle->m_driver_procinfo = NULL;
}
if(handle->m_suppressed_comms) if(handle->m_suppressed_comms)
{ {
@ -1581,6 +1789,140 @@ static int32_t scap_next_nodriver(scap_t* handle, OUT scap_evt** pevent, OUT uin
} }
#endif // _WIN32 #endif // _WIN32
static inline uint64_t get_timestamp_ns()
{
uint64_t ts;
#ifdef _WIN32
FILETIME ft;
static const uint64_t EPOCH = ((uint64_t) 116444736000000000ULL);
GetSystemTimePreciseAsFileTime(&ft);
uint64_t ftl = (((uint64_t)ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
ftl -= EPOCH;
ts = ftl * 100;
#else
struct timeval tv;
gettimeofday(&tv, NULL);
ts = tv.tv_sec * (uint64_t) 1000000000 + tv.tv_usec * 1000;
#endif
return ts;
}
static int32_t scap_next_plugin(scap_t* handle, OUT scap_evt** pevent, OUT uint16_t* pcpuid)
{
ss_plugin_event *plugin_evt;
int32_t res = SCAP_FAILURE;
if(handle->m_input_plugin_batch_idx >= handle->m_input_plugin_batch_nevts)
{
if(handle->m_input_plugin_last_batch_res != SS_PLUGIN_SUCCESS)
{
if(handle->m_input_plugin_last_batch_res != SCAP_TIMEOUT && handle->m_input_plugin_last_batch_res != SCAP_EOF)
{
const char *errstr = handle->m_input_plugin->get_last_error(handle->m_input_plugin->state);
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "%s", errstr);
}
int32_t tres = handle->m_input_plugin_last_batch_res;
handle->m_input_plugin_last_batch_res = SCAP_SUCCESS;
return tres;
}
int32_t plugin_res = handle->m_input_plugin->next_batch(handle->m_input_plugin->state,
handle->m_input_plugin->handle,
&(handle->m_input_plugin_batch_nevts),
&(handle->m_input_plugin_batch_evts));
handle->m_input_plugin_last_batch_res = plugin_rc_to_scap_rc(plugin_res);
if(handle->m_input_plugin_batch_nevts == 0)
{
if(handle->m_input_plugin_last_batch_res == SCAP_SUCCESS)
{
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "unexpected 0 size event returned by plugin %s", handle->m_input_plugin->name);
ASSERT(false);
return SCAP_FAILURE;
}
else
{
if(handle->m_input_plugin_last_batch_res != SCAP_TIMEOUT && handle->m_input_plugin_last_batch_res != SCAP_EOF)
{
const char *errstr = handle->m_input_plugin->get_last_error(handle->m_input_plugin->state);
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "%s", errstr);
}
return handle->m_input_plugin_last_batch_res;
}
}
handle->m_input_plugin_batch_idx = 0;
}
uint32_t pos = handle->m_input_plugin_batch_idx;
plugin_evt = &(handle->m_input_plugin_batch_evts[pos]);
handle->m_input_plugin_batch_idx++;
res = SCAP_SUCCESS;
/*
* | scap_evt | len_id (4B) | len_pl (4B) | id | payload |
* Note: we need to use 4B for len_id too because the PPME_PLUGINEVENT_E has
* EF_LARGE_PAYLOAD flag!
*/
uint32_t reqsize = sizeof(scap_evt) + 4 + 4 + 4 + plugin_evt->datalen;
if(handle->m_input_plugin_evt_storage_len < reqsize)
{
uint8_t *tmp = (uint8_t*)realloc(handle->m_input_plugin_evt_storage, reqsize);
if (tmp)
{
handle->m_input_plugin_evt_storage = tmp;
handle->m_input_plugin_evt_storage_len = reqsize;
}
else
{
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "%s", "failed to alloc space for plugin storage");
ASSERT(false);
return SCAP_FAILURE;
}
}
scap_evt* evt = (scap_evt*)handle->m_input_plugin_evt_storage;
evt->len = reqsize;
evt->tid = -1;
evt->type = PPME_PLUGINEVENT_E;
evt->nparams = 2;
uint8_t* buf = handle->m_input_plugin_evt_storage + sizeof(scap_evt);
const uint32_t plugin_id_size = 4;
memcpy(buf, &plugin_id_size, sizeof(plugin_id_size));
buf += sizeof(plugin_id_size);
uint32_t datalen = plugin_evt->datalen;
memcpy(buf, &(datalen), sizeof(datalen));
buf += sizeof(datalen);
memcpy(buf, &(handle->m_input_plugin->id), sizeof(handle->m_input_plugin->id));
buf += sizeof(handle->m_input_plugin->id);
memcpy(buf, plugin_evt->data, plugin_evt->datalen);
if(plugin_evt->ts != UINT64_MAX)
{
evt->ts = plugin_evt->ts;
}
else
{
evt->ts = get_timestamp_ns();
}
*pevent = evt;
return res;
}
uint64_t scap_max_buf_used(scap_t* handle) uint64_t scap_max_buf_used(scap_t* handle)
{ {
#if defined(HAS_CAPTURE) && !defined(CYGWING_AGENT) #if defined(HAS_CAPTURE) && !defined(CYGWING_AGENT)
@ -1623,6 +1965,9 @@ int32_t scap_next(scap_t* handle, OUT scap_evt** pevent, OUT uint16_t* pcpuid)
res = scap_next_nodriver(handle, pevent, pcpuid); res = scap_next_nodriver(handle, pevent, pcpuid);
break; break;
#endif #endif
case SCAP_MODE_PLUGIN:
res = scap_next_plugin(handle, pevent, pcpuid);
break;
case SCAP_MODE_NONE: case SCAP_MODE_NONE:
res = SCAP_FAILURE; res = SCAP_FAILURE;
} }
@ -2058,7 +2403,7 @@ int64_t scap_get_readfile_offset(scap_t* handle)
return -1; return -1;
} }
return gzoffset(handle->m_file); return scap_reader_offset(handle->m_reader);
} }
#ifndef CYGWING_AGENT #ifndef CYGWING_AGENT
@ -2101,9 +2446,7 @@ static int32_t scap_handle_eventmask(scap_t* handle, uint32_t op, uint32_t event
if(handle->m_bpf) if(handle->m_bpf)
{ {
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "eventmask not supported on bpf"); return scap_bpf_handle_event_mask(handle, op, event_id);
ASSERT(false);
return SCAP_FAILURE;
} }
else else
{ {
@ -2256,12 +2599,11 @@ int32_t scap_disable_dynamic_snaplen(scap_t* handle)
const char* scap_get_host_root() const char* scap_get_host_root()
{ {
char* p = getenv("SYSDIG_HOST_ROOT"); char* p = getenv(SCAP_HOST_ROOT_ENV_VAR_NAME);
static char env_str[SCAP_MAX_PATH_SIZE + 1]; static char env_str[SCAP_MAX_PATH_SIZE + 1];
static bool inited = false; static bool inited = false;
if (! inited) { if (! inited) {
strncpy(env_str, p ? p : "", SCAP_MAX_PATH_SIZE); strlcpy(env_str, p ? p : "", sizeof(env_str));
env_str[SCAP_MAX_PATH_SIZE] = '\0';
inited = true; inited = true;
} }
@ -2382,10 +2724,13 @@ int32_t scap_enable_simpledriver_mode(scap_t* handle)
if(handle->m_bpf) if(handle->m_bpf)
{ {
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "setting simpledriver mode not supported on bpf"); if (scap_bpf_set_simple_mode(handle))
{
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_enable_simpledriver_mode failed");
ASSERT(false); ASSERT(false);
return SCAP_FAILURE; return SCAP_FAILURE;
} }
}
else else
{ {
if(ioctl(handle->m_devs[0].m_fd, PPM_IOCTL_SET_SIMPLE_MODE)) if(ioctl(handle->m_devs[0].m_fd, PPM_IOCTL_SET_SIMPLE_MODE))
@ -2458,7 +2803,7 @@ wh_t* scap_get_wmi_handle(scap_t* handle)
const char *scap_get_bpf_probe_from_env() const char *scap_get_bpf_probe_from_env()
{ {
return getenv(SYSDIG_BPF_PROBE_ENV); return getenv(SCAP_BPF_PROBE_ENV_VAR_NAME);
} }
bool scap_get_bpf_enabled(scap_t *handle) bool scap_get_bpf_enabled(scap_t *handle)

View File

@ -65,6 +65,8 @@ struct iovec;
#define MAP_FAILED (void*)-1 #define MAP_FAILED (void*)-1
#endif #endif
#include "plugin_info.h"
// //
// Return types // Return types
// //
@ -223,6 +225,7 @@ typedef struct scap_threadinfo
char comm[SCAP_MAX_PATH_SIZE+1]; ///< Command name (e.g. "top") char comm[SCAP_MAX_PATH_SIZE+1]; ///< Command name (e.g. "top")
char exe[SCAP_MAX_PATH_SIZE+1]; ///< argv[0] (e.g. "sshd: user@pts/4") char exe[SCAP_MAX_PATH_SIZE+1]; ///< argv[0] (e.g. "sshd: user@pts/4")
char exepath[SCAP_MAX_PATH_SIZE+1]; ///< full executable path char exepath[SCAP_MAX_PATH_SIZE+1]; ///< full executable path
bool exe_writable; ///< true if the original executable is writable by the same user that spawned it.
char args[SCAP_MAX_ARGS_SIZE+1]; ///< Command line arguments (e.g. "-d1") char args[SCAP_MAX_ARGS_SIZE+1]; ///< Command line arguments (e.g. "-d1")
uint16_t args_len; ///< Command line arguments length uint16_t args_len; ///< Command line arguments length
char env[SCAP_MAX_ENV_SIZE+1]; ///< Environment char env[SCAP_MAX_ENV_SIZE+1]; ///< Environment
@ -288,8 +291,21 @@ typedef enum {
* returned. * returned.
*/ */
SCAP_MODE_NODRIVER, SCAP_MODE_NODRIVER,
/*!
* Do not read system call data. Events come from the configured input plugin.
*/
SCAP_MODE_PLUGIN,
} scap_mode_t; } scap_mode_t;
/*!
\brief Argument for scap_open
Set any PPM_SC syscall idx to true to enable its tracing at driver level,
otherwise syscalls are not traced (so called "uninteresting syscalls").
*/
typedef struct {
bool ppm_sc[PPM_SC_MAX];
} interesting_ppm_sc_set;
typedef struct scap_open_args typedef struct scap_open_args
{ {
scap_mode_t mode; scap_mode_t mode;
@ -305,6 +321,10 @@ typedef struct scap_open_args
// You can provide additional comm // You can provide additional comm
// values via scap_suppress_events_comm(). // values via scap_suppress_events_comm().
bool udig; ///< If true, UDIG will be used for event capture. Otherwise, the kernel driver will be used. bool udig; ///< If true, UDIG will be used for event capture. Otherwise, the kernel driver will be used.
interesting_ppm_sc_set ppm_sc_of_interest;
source_plugin_info* input_plugin; ///< use this to configure a source plugin that will produce the events for this capture
char* input_plugin_params; ///< optional parameters string for the source plugin pointed by src_plugin
}scap_open_args; }scap_open_args;
@ -503,11 +523,14 @@ typedef enum scap_dump_flags
SCAP_DF_NONE = 0, SCAP_DF_NONE = 0,
SCAP_DF_STATE_ONLY = 1, ///< The event should be used for state update but it should SCAP_DF_STATE_ONLY = 1, ///< The event should be used for state update but it should
///< not be shown to the user ///< not be shown to the user
SCAP_DF_TRACER = (1 << 1) ///< This event is a tracer SCAP_DF_TRACER = (1 << 1), ///< This event is a tracer
SCAP_DF_LARGE = (1 << 2) ///< This event has large payload (up to UINT_MAX Bytes, ie 4GB)
}scap_dump_flags; }scap_dump_flags;
typedef struct scap_dumper scap_dumper_t; typedef struct scap_dumper scap_dumper_t;
typedef struct scap_reader scap_reader_t;
/*! /*!
\brief System call description struct. \brief System call description struct.
*/ */
@ -618,6 +641,18 @@ scap_t* scap_open(scap_open_args args, char *error, int32_t *rc);
*/ */
void scap_close(scap_t* handle); void scap_close(scap_t* handle);
/*!
\brief Restart the current event capture.
Only supported for captures in SCAP_MODE_CAPTURE mode.
This deinitializes the scap internal state, and then re-initializes
it by trying to read the scap header section. The underlying instance
of scap_reader_t is preserved, and the header section is read starting
from its current offset.
\param handle Handle to the capture instance.
*/
uint32_t scap_restart_capture(scap_t* handle);
/*! /*!
\brief Retrieve the OS platform for the given capture handle. \brief Retrieve the OS platform for the given capture handle.

View File

@ -140,6 +140,19 @@ static int bpf_map_create(enum bpf_map_type map_type,
return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
} }
static int bpf_map_freeze(int fd)
{
union bpf_attr attr;
bzero(&attr, sizeof(attr));
attr.map_fd = fd;
/* Do not check for errors as BPF_MAP_FREEZE was introduced in kernel 5.2 */
sys_bpf(BPF_MAP_FREEZE, &attr, sizeof(attr));
return SCAP_SUCCESS;
}
static int bpf_load_program(const struct bpf_insn *insns, static int bpf_load_program(const struct bpf_insn *insns,
enum bpf_prog_type type, enum bpf_prog_type type,
size_t insns_cnt, size_t insns_cnt,
@ -750,16 +763,22 @@ static int32_t populate_syscall_routing_table_map(scap_t *handle)
} }
} }
return SCAP_SUCCESS; return bpf_map_freeze(handle->m_bpf_map_fds[SYSDIG_SYSCALL_CODE_ROUTING_TABLE]);
} }
static int32_t populate_syscall_table_map(scap_t *handle) static int32_t populate_syscall_table_map(scap_t *handle)
{ {
static const struct syscall_evt_pair uninterested_pair = { .flags = UF_UNINTERESTING };
int j; int j;
for(j = 0; j < SYSCALL_TABLE_SIZE; ++j) for(j = 0; j < SYSCALL_TABLE_SIZE; ++j)
{ {
const struct syscall_evt_pair *p = &g_syscall_table[j]; const struct syscall_evt_pair *p = &g_syscall_table[j];
if (!handle->syscalls_of_interest[j])
{
p = &uninterested_pair;
}
if(bpf_map_update_elem(handle->m_bpf_map_fds[SYSDIG_SYSCALL_TABLE], &j, p, BPF_ANY) != 0) if(bpf_map_update_elem(handle->m_bpf_map_fds[SYSDIG_SYSCALL_TABLE], &j, p, BPF_ANY) != 0)
{ {
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SYSCALL_TABLE bpf_map_update_elem < 0"); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SYSCALL_TABLE bpf_map_update_elem < 0");
@ -784,7 +803,7 @@ static int32_t populate_event_table_map(scap_t *handle)
} }
} }
return SCAP_SUCCESS; return bpf_map_freeze(handle->m_bpf_map_fds[SYSDIG_EVENT_INFO_TABLE]);
} }
static int32_t populate_fillers_table_map(scap_t *handle) static int32_t populate_fillers_table_map(scap_t *handle)
@ -810,7 +829,7 @@ static int32_t populate_fillers_table_map(scap_t *handle)
} }
} }
return SCAP_SUCCESS; return bpf_map_freeze(handle->m_bpf_map_fds[SYSDIG_FILLERS_TABLE]);
} }
// //
@ -1474,3 +1493,52 @@ int32_t scap_bpf_get_n_tracepoint_hit(scap_t* handle, long* ret)
return SCAP_SUCCESS; return SCAP_SUCCESS;
} }
int32_t scap_bpf_set_simple_mode(scap_t* handle)
{
int j;
for(j = 0; j < SYSCALL_TABLE_SIZE; ++j)
{
const struct syscall_evt_pair *p = &g_syscall_table[j];
if(!(p->flags & UF_SIMPLEDRIVER_KEEP))
{
handle->syscalls_of_interest[j] = false;
}
}
return populate_syscall_table_map(handle);
}
int32_t scap_bpf_handle_event_mask(scap_t *handle, uint32_t op, uint32_t event_id) {
int j;
bool quit = false;
for(j = 0; j < SYSCALL_TABLE_SIZE && !quit; ++j)
{
/*
* In case PPM_IOCTL_MASK_ZERO_EVENTS is called, event_id will be 0. Set every syscall to false in that case.
* Otherwise, check {enter,exit} event for each syscall to see if it matches the requested event_id.
*/
if (event_id == 0 || g_syscall_table[j].enter_event_type == event_id || g_syscall_table[j].exit_event_type == event_id)
{
switch(op)
{
case PPM_IOCTL_MASK_ZERO_EVENTS:
handle->syscalls_of_interest[j] = false;
break;
case PPM_IOCTL_MASK_SET_EVENT:
handle->syscalls_of_interest[j] = true;
quit = true;
break;
case PPM_IOCTL_MASK_UNSET_EVENT:
handle->syscalls_of_interest[j] = false;
quit = true;
break;
default:
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "%s(%d) internal error", __FUNCTION__, op);
ASSERT(false);
return SCAP_FAILURE;
break;
}
}
}
return populate_syscall_table_map(handle);
}

View File

@ -46,6 +46,8 @@ int32_t scap_bpf_stop_dropping_mode(scap_t* handle);
int32_t scap_bpf_enable_tracers_capture(scap_t* handle); int32_t scap_bpf_enable_tracers_capture(scap_t* handle);
int32_t scap_bpf_get_stats(scap_t* handle, OUT scap_stats* stats); int32_t scap_bpf_get_stats(scap_t* handle, OUT scap_stats* stats);
int32_t scap_bpf_get_n_tracepoint_hit(scap_t* handle, long* ret); int32_t scap_bpf_get_n_tracepoint_hit(scap_t* handle, long* ret);
int32_t scap_bpf_set_simple_mode(scap_t* handle);
int32_t scap_bpf_handle_event_mask(scap_t *handle, uint32_t op, uint32_t event_id);
static inline scap_evt *scap_bpf_evt_from_perf_sample(void *evt) static inline scap_evt *scap_bpf_evt_from_perf_sample(void *evt)
{ {

View File

@ -20,6 +20,7 @@ limitations under the License.
#include "scap.h" #include "scap.h"
#include "scap-int.h" #include "scap-int.h"
#include "scap_savefile.h" #include "scap_savefile.h"
#include "../common/strlcpy.h"
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <fcntl.h> #include <fcntl.h>
@ -385,21 +386,21 @@ int32_t scap_fd_write_to_disk(scap_t *handle, scap_fdinfo *fdi, scap_dumper_t *d
return SCAP_SUCCESS; return SCAP_SUCCESS;
} }
uint32_t scap_fd_read_prop_from_disk(scap_t *handle, OUT void *target, size_t expected_size, OUT size_t *nbytes, gzFile f) uint32_t scap_fd_read_prop_from_disk(scap_t *handle, OUT void *target, size_t expected_size, OUT size_t *nbytes, scap_reader_t* r)
{ {
size_t readsize; size_t readsize;
readsize = gzread(f, target, (unsigned int)expected_size); readsize = scap_reader_read(r, target, (unsigned int)expected_size);
CHECK_READ_SIZE(readsize, expected_size); CHECK_READ_SIZE(readsize, expected_size);
(*nbytes) += readsize; (*nbytes) += readsize;
return SCAP_SUCCESS; return SCAP_SUCCESS;
} }
uint32_t scap_fd_read_fname_from_disk(scap_t* handle, char* fname,OUT size_t* nbytes, gzFile f) uint32_t scap_fd_read_fname_from_disk(scap_t* handle, char* fname,OUT size_t* nbytes, scap_reader_t* r)
{ {
size_t readsize; size_t readsize;
uint16_t stlen; uint16_t stlen;
readsize = gzread(f, &(stlen), sizeof(uint16_t)); readsize = scap_reader_read(r, &(stlen), sizeof(uint16_t));
CHECK_READ_SIZE(readsize, sizeof(uint16_t)); CHECK_READ_SIZE(readsize, sizeof(uint16_t));
if(stlen >= SCAP_MAX_PATH_SIZE) if(stlen >= SCAP_MAX_PATH_SIZE)
@ -410,7 +411,7 @@ uint32_t scap_fd_read_fname_from_disk(scap_t* handle, char* fname,OUT size_t* nb
(*nbytes) += readsize; (*nbytes) += readsize;
readsize = gzread(f, fname, stlen); readsize = scap_reader_read(r, fname, stlen);
CHECK_READ_SIZE(readsize, stlen); CHECK_READ_SIZE(readsize, stlen);
(*nbytes) += stlen; (*nbytes) += stlen;
@ -424,7 +425,7 @@ uint32_t scap_fd_read_fname_from_disk(scap_t* handle, char* fname,OUT size_t* nb
// Populate the given fd by reading the info from disk // Populate the given fd by reading the info from disk
// Returns the number of read bytes. // Returns the number of read bytes.
// //
uint32_t scap_fd_read_from_disk(scap_t *handle, OUT scap_fdinfo *fdi, OUT size_t *nbytes, uint32_t block_type, gzFile f) uint32_t scap_fd_read_from_disk(scap_t *handle, OUT scap_fdinfo *fdi, OUT size_t *nbytes, uint32_t block_type, scap_reader_t* r)
{ {
uint8_t type; uint8_t type;
uint32_t toread; uint32_t toread;
@ -433,10 +434,10 @@ uint32_t scap_fd_read_from_disk(scap_t *handle, OUT scap_fdinfo *fdi, OUT size_t
uint32_t res = SCAP_SUCCESS; uint32_t res = SCAP_SUCCESS;
*nbytes = 0; *nbytes = 0;
if((block_type == FDL_BLOCK_TYPE_V2 && scap_fd_read_prop_from_disk(handle, &sub_len, sizeof(uint32_t), nbytes, f)) || if((block_type == FDL_BLOCK_TYPE_V2 && scap_fd_read_prop_from_disk(handle, &sub_len, sizeof(uint32_t), nbytes, r)) ||
scap_fd_read_prop_from_disk(handle, &(fdi->fd), sizeof(fdi->fd), nbytes, f) || scap_fd_read_prop_from_disk(handle, &(fdi->fd), sizeof(fdi->fd), nbytes, r) ||
scap_fd_read_prop_from_disk(handle, &(fdi->ino), sizeof(fdi->ino), nbytes, f) || scap_fd_read_prop_from_disk(handle, &(fdi->ino), sizeof(fdi->ino), nbytes, r) ||
scap_fd_read_prop_from_disk(handle, &type, sizeof(uint8_t), nbytes, f)) scap_fd_read_prop_from_disk(handle, &type, sizeof(uint8_t), nbytes, r))
{ {
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Could not read prop block for fd"); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Could not read prop block for fd");
return SCAP_FAILURE; return SCAP_FAILURE;
@ -456,11 +457,11 @@ uint32_t scap_fd_read_from_disk(scap_t *handle, OUT scap_fdinfo *fdi, OUT size_t
switch(fdi->type) switch(fdi->type)
{ {
case SCAP_FD_IPV4_SOCK: case SCAP_FD_IPV4_SOCK:
if(gzread(f, &(fdi->info.ipv4info.sip), sizeof(uint32_t)) != sizeof(uint32_t) || if(scap_reader_read(r, &(fdi->info.ipv4info.sip), sizeof(uint32_t)) != sizeof(uint32_t) ||
gzread(f, &(fdi->info.ipv4info.dip), sizeof(uint32_t)) != sizeof(uint32_t) || scap_reader_read(r, &(fdi->info.ipv4info.dip), sizeof(uint32_t)) != sizeof(uint32_t) ||
gzread(f, &(fdi->info.ipv4info.sport), sizeof(uint16_t)) != sizeof(uint16_t) || scap_reader_read(r, &(fdi->info.ipv4info.sport), sizeof(uint16_t)) != sizeof(uint16_t) ||
gzread(f, &(fdi->info.ipv4info.dport), sizeof(uint16_t)) != sizeof(uint16_t) || scap_reader_read(r, &(fdi->info.ipv4info.dport), sizeof(uint16_t)) != sizeof(uint16_t) ||
gzread(f, &(fdi->info.ipv4info.l4proto), sizeof(uint8_t)) != sizeof(uint8_t)) scap_reader_read(r, &(fdi->info.ipv4info.l4proto), sizeof(uint8_t)) != sizeof(uint8_t))
{ {
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error reading the fd info from file (1)"); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error reading the fd info from file (1)");
return SCAP_FAILURE; return SCAP_FAILURE;
@ -470,9 +471,9 @@ uint32_t scap_fd_read_from_disk(scap_t *handle, OUT scap_fdinfo *fdi, OUT size_t
break; break;
case SCAP_FD_IPV4_SERVSOCK: case SCAP_FD_IPV4_SERVSOCK:
if(gzread(f, &(fdi->info.ipv4serverinfo.ip), sizeof(uint32_t)) != sizeof(uint32_t) || if(scap_reader_read(r, &(fdi->info.ipv4serverinfo.ip), sizeof(uint32_t)) != sizeof(uint32_t) ||
gzread(f, &(fdi->info.ipv4serverinfo.port), sizeof(uint16_t)) != sizeof(uint16_t) || scap_reader_read(r, &(fdi->info.ipv4serverinfo.port), sizeof(uint16_t)) != sizeof(uint16_t) ||
gzread(f, &(fdi->info.ipv4serverinfo.l4proto), sizeof(uint8_t)) != sizeof(uint8_t)) scap_reader_read(r, &(fdi->info.ipv4serverinfo.l4proto), sizeof(uint8_t)) != sizeof(uint8_t))
{ {
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error reading the fd info from file (2)"); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error reading the fd info from file (2)");
return SCAP_FAILURE; return SCAP_FAILURE;
@ -481,11 +482,11 @@ uint32_t scap_fd_read_from_disk(scap_t *handle, OUT scap_fdinfo *fdi, OUT size_t
(*nbytes) += (sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint8_t)); (*nbytes) += (sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint8_t));
break; break;
case SCAP_FD_IPV6_SOCK: case SCAP_FD_IPV6_SOCK:
if(gzread(f, (char*)fdi->info.ipv6info.sip, sizeof(uint32_t) * 4) != sizeof(uint32_t) * 4 || if(scap_reader_read(r, (char*)fdi->info.ipv6info.sip, sizeof(uint32_t) * 4) != sizeof(uint32_t) * 4 ||
gzread(f, (char*)fdi->info.ipv6info.dip, sizeof(uint32_t) * 4) != sizeof(uint32_t) * 4 || scap_reader_read(r, (char*)fdi->info.ipv6info.dip, sizeof(uint32_t) * 4) != sizeof(uint32_t) * 4 ||
gzread(f, &(fdi->info.ipv6info.sport), sizeof(uint16_t)) != sizeof(uint16_t) || scap_reader_read(r, &(fdi->info.ipv6info.sport), sizeof(uint16_t)) != sizeof(uint16_t) ||
gzread(f, &(fdi->info.ipv6info.dport), sizeof(uint16_t)) != sizeof(uint16_t) || scap_reader_read(r, &(fdi->info.ipv6info.dport), sizeof(uint16_t)) != sizeof(uint16_t) ||
gzread(f, &(fdi->info.ipv6info.l4proto), sizeof(uint8_t)) != sizeof(uint8_t)) scap_reader_read(r, &(fdi->info.ipv6info.l4proto), sizeof(uint8_t)) != sizeof(uint8_t))
{ {
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (fi3)"); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (fi3)");
} }
@ -496,9 +497,9 @@ uint32_t scap_fd_read_from_disk(scap_t *handle, OUT scap_fdinfo *fdi, OUT size_t
sizeof(uint8_t)); // l4proto sizeof(uint8_t)); // l4proto
break; break;
case SCAP_FD_IPV6_SERVSOCK: case SCAP_FD_IPV6_SERVSOCK:
if(gzread(f, (char*)fdi->info.ipv6serverinfo.ip, sizeof(uint32_t) * 4) != sizeof(uint32_t) * 4|| if(scap_reader_read(r, (char*)fdi->info.ipv6serverinfo.ip, sizeof(uint32_t) * 4) != sizeof(uint32_t) * 4||
gzread(f, &(fdi->info.ipv6serverinfo.port), sizeof(uint16_t)) != sizeof(uint16_t) || scap_reader_read(r, &(fdi->info.ipv6serverinfo.port), sizeof(uint16_t)) != sizeof(uint16_t) ||
gzread(f, &(fdi->info.ipv6serverinfo.l4proto), sizeof(uint8_t)) != sizeof(uint8_t)) scap_reader_read(r, &(fdi->info.ipv6serverinfo.l4proto), sizeof(uint8_t)) != sizeof(uint8_t))
{ {
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (fi4)"); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (fi4)");
} }
@ -507,30 +508,30 @@ uint32_t scap_fd_read_from_disk(scap_t *handle, OUT scap_fdinfo *fdi, OUT size_t
sizeof(uint8_t)); // l4proto sizeof(uint8_t)); // l4proto
break; break;
case SCAP_FD_UNIX_SOCK: case SCAP_FD_UNIX_SOCK:
if(gzread(f, &(fdi->info.unix_socket_info.source), sizeof(uint64_t)) != sizeof(uint64_t) || if(scap_reader_read(r, &(fdi->info.unix_socket_info.source), sizeof(uint64_t)) != sizeof(uint64_t) ||
gzread(f, &(fdi->info.unix_socket_info.destination), sizeof(uint64_t)) != sizeof(uint64_t)) scap_reader_read(r, &(fdi->info.unix_socket_info.destination), sizeof(uint64_t)) != sizeof(uint64_t))
{ {
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error reading the fd info from file (fi5)"); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error reading the fd info from file (fi5)");
return SCAP_FAILURE; return SCAP_FAILURE;
} }
(*nbytes) += (sizeof(uint64_t) + sizeof(uint64_t)); (*nbytes) += (sizeof(uint64_t) + sizeof(uint64_t));
res = scap_fd_read_fname_from_disk(handle, fdi->info.unix_socket_info.fname, nbytes, f); res = scap_fd_read_fname_from_disk(handle, fdi->info.unix_socket_info.fname, nbytes, r);
break; break;
case SCAP_FD_FILE_V2: case SCAP_FD_FILE_V2:
if(gzread(f, &(fdi->info.regularinfo.open_flags), sizeof(uint32_t)) != sizeof(uint32_t)) if(scap_reader_read(r, &(fdi->info.regularinfo.open_flags), sizeof(uint32_t)) != sizeof(uint32_t))
{ {
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error reading the fd info from file (fi1)"); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error reading the fd info from file (fi1)");
return SCAP_FAILURE; return SCAP_FAILURE;
} }
(*nbytes) += sizeof(uint32_t); (*nbytes) += sizeof(uint32_t);
res = scap_fd_read_fname_from_disk(handle, fdi->info.regularinfo.fname, nbytes, f); res = scap_fd_read_fname_from_disk(handle, fdi->info.regularinfo.fname, nbytes, r);
if (!sub_len || (sub_len < *nbytes + sizeof(uint32_t))) if (!sub_len || (sub_len < *nbytes + sizeof(uint32_t)))
{ {
break; break;
} }
if(gzread(f, &(fdi->info.regularinfo.dev), sizeof(uint32_t)) != sizeof(uint32_t)) if(scap_reader_read(r, &(fdi->info.regularinfo.dev), sizeof(uint32_t)) != sizeof(uint32_t))
{ {
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error reading the fd info from file (dev)"); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error reading the fd info from file (dev)");
return SCAP_FAILURE; return SCAP_FAILURE;
@ -547,7 +548,7 @@ uint32_t scap_fd_read_from_disk(scap_t *handle, OUT scap_fdinfo *fdi, OUT size_t
case SCAP_FD_INOTIFY: case SCAP_FD_INOTIFY:
case SCAP_FD_TIMERFD: case SCAP_FD_TIMERFD:
case SCAP_FD_NETLINK: case SCAP_FD_NETLINK:
res = scap_fd_read_fname_from_disk(handle, fdi->info.fname,nbytes,f); res = scap_fd_read_fname_from_disk(handle, fdi->info.fname,nbytes, r);
break; break;
case SCAP_FD_UNKNOWN: case SCAP_FD_UNKNOWN:
ASSERT(false); ASSERT(false);
@ -566,7 +567,7 @@ uint32_t scap_fd_read_from_disk(scap_t *handle, OUT scap_fdinfo *fdi, OUT size_t
return SCAP_FAILURE; return SCAP_FAILURE;
} }
toread = (uint32_t)(sub_len - *nbytes); toread = (uint32_t)(sub_len - *nbytes);
fseekres = (int)gzseek(f, (long)toread, SEEK_CUR); fseekres = (int)scap_reader_seek(r, (long)toread, SEEK_CUR);
if(fseekres == -1) if(fseekres == -1)
{ {
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "corrupted input file. Can't skip %u bytes.", snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "corrupted input file. Can't skip %u bytes.",
@ -750,7 +751,7 @@ int32_t scap_fd_handle_pipe(scap_t *handle, char *fname, scap_threadinfo *tinfo,
} }
ino = sb.st_ino; ino = sb.st_ino;
} }
strncpy(fdi->info.fname, link_name, SCAP_MAX_PATH_SIZE); strlcpy(fdi->info.fname, link_name, sizeof(fdi->info.fname));
fdi->ino = ino; fdi->ino = ino;
return scap_add_fd_to_proc_table(handle, tinfo, fdi, error); return scap_add_fd_to_proc_table(handle, tinfo, fdi, error);
@ -976,11 +977,11 @@ int32_t scap_fd_handle_regular_file(scap_t *handle, char *fname, scap_threadinfo
else if(fdi->type == SCAP_FD_FILE_V2) else if(fdi->type == SCAP_FD_FILE_V2)
{ {
scap_fd_flags_file(handle, fdi, procdir); scap_fd_flags_file(handle, fdi, procdir);
strncpy(fdi->info.regularinfo.fname, link_name, SCAP_MAX_PATH_SIZE); strlcpy(fdi->info.regularinfo.fname, link_name, sizeof(fdi->info.regularinfo.fname));
} }
else else
{ {
strncpy(fdi->info.fname, link_name, SCAP_MAX_PATH_SIZE); strlcpy(fdi->info.fname, link_name, sizeof(fdi->info.fname));
} }
return scap_add_fd_to_proc_table(handle, tinfo, fdi, error); return scap_add_fd_to_proc_table(handle, tinfo, fdi, error);
@ -1034,7 +1035,7 @@ int32_t scap_fd_handle_socket(scap_t *handle, char *fname, scap_threadinfo *tinf
link_name[r] = '\0'; link_name[r] = '\0';
strncpy(fdi->info.fname, link_name, SCAP_MAX_PATH_SIZE); strlcpy(fdi->info.fname, link_name, sizeof(fdi->info.fname));
// link name for sockets should be of the format socket:[ino] // link name for sockets should be of the format socket:[ino]
if(1 != sscanf(link_name, "socket:[%"PRIi64"]", &ino)) if(1 != sscanf(link_name, "socket:[%"PRIi64"]", &ino))
@ -1168,7 +1169,7 @@ int32_t scap_fd_read_unix_sockets_from_proc_fs(scap_t *handle, const char* filen
token = strtok_r(NULL, delimiters, &scratch); token = strtok_r(NULL, delimiters, &scratch);
if(NULL != token) if(NULL != token)
{ {
strncpy(fdinfo->info.unix_socket_info.fname, token, SCAP_MAX_PATH_SIZE); strlcpy(fdinfo->info.unix_socket_info.fname, token, sizeof(fdinfo->info.unix_socket_info.fname));
} }
else else
{ {

View File

@ -19,6 +19,7 @@ limitations under the License.
#include "scap.h" #include "scap.h"
#include "scap-int.h" #include "scap-int.h"
#include "../common/strlcpy.h"
#if defined(HAS_CAPTURE) && !defined(_WIN32) #if defined(HAS_CAPTURE) && !defined(_WIN32)
#include <sys/types.h> #include <sys/types.h>
@ -168,7 +169,7 @@ int32_t scap_create_iflist(scap_t* handle)
#else #else
handle->m_addrlist->v4list[ifcnt4].bcast = 0; handle->m_addrlist->v4list[ifcnt4].bcast = 0;
#endif #endif
strncpy(handle->m_addrlist->v4list[ifcnt4].ifname, tempIfAddr->ifa_name, SCAP_MAX_PATH_SIZE); strlcpy(handle->m_addrlist->v4list[ifcnt4].ifname, tempIfAddr->ifa_name, sizeof(handle->m_addrlist->v4list[ifcnt4].ifname));
handle->m_addrlist->v4list[ifcnt4].ifnamelen = strlen(tempIfAddr->ifa_name); handle->m_addrlist->v4list[ifcnt4].ifnamelen = strlen(tempIfAddr->ifa_name);
handle->m_addrlist->v4list[ifcnt4].linkspeed = 0; handle->m_addrlist->v4list[ifcnt4].linkspeed = 0;
@ -210,7 +211,7 @@ int32_t scap_create_iflist(scap_t* handle)
handle->m_addrlist->v4list[ifcnt4].bcast = 0; handle->m_addrlist->v4list[ifcnt4].bcast = 0;
#endif #endif
strncpy(handle->m_addrlist->v6list[ifcnt6].ifname, tempIfAddr->ifa_name, SCAP_MAX_PATH_SIZE); strlcpy(handle->m_addrlist->v6list[ifcnt6].ifname, tempIfAddr->ifa_name, sizeof(handle->m_addrlist->v6list[ifcnt6].ifname));
handle->m_addrlist->v6list[ifcnt6].ifnamelen = strlen(tempIfAddr->ifa_name); handle->m_addrlist->v6list[ifcnt6].ifnamelen = strlen(tempIfAddr->ifa_name);
handle->m_addrlist->v6list[ifcnt6].linkspeed = 0; handle->m_addrlist->v6list[ifcnt6].linkspeed = 0;

View File

@ -28,6 +28,9 @@ limitations under the License.
#include <sys/syscall.h> #include <sys/syscall.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include "unixid.h"
#endif // CYGWING_AGENT #endif // CYGWING_AGENT
#endif // HAS_CAPTURE #endif // HAS_CAPTURE
@ -561,6 +564,65 @@ int32_t scap_proc_fill_loginuid(scap_t *handle, struct scap_threadinfo* tinfo, c
} }
} }
int32_t scap_proc_fill_exe_writable(scap_t* handle, struct scap_threadinfo* tinfo, uint32_t uid, uint32_t gid, const char *procdirname, const char *exetarget)
{
char proc_exe_path[SCAP_MAX_PATH_SIZE];
struct stat targetstat;
snprintf(proc_exe_path, sizeof(proc_exe_path), "%sroot%s", procdirname, exetarget);
// if the file doesn't exist we can't determine if it was writable, assume false
if(stat(proc_exe_path, &targetstat) < 0)
{
return SCAP_SUCCESS;
}
// if you're the user owning the file you can chmod, so you can effectively write to it
if(targetstat.st_uid == uid) {
tinfo->exe_writable = true;
return SCAP_SUCCESS;
}
uid_t orig_uid = geteuid();
uid_t orig_gid = getegid();
//
// In order to check whether the current user can access the file we need to temporarily
// set the effective uid and gid of our thread to the target ones and then check access,
// but keep in mind that:
// - seteuid()/setegid() libc functions change the euid/egid of the whole process, not just
// the current thread
// - setfsuid()/setfsgid() operate on threads but cannot be paired with access(),
// so we would need to open() the file, but opening executable files in use may result
// in "text file busy" errors
//
// Therefore we need to directly call the appropriate setresuid syscall that operate on threads,
// implemented in the thread_seteuid() and thread_setegid() functions.
//
if(thread_seteuid(uid) != -1 && thread_setegid(gid) != -1) {
if(faccessat(0, proc_exe_path, W_OK, AT_EACCESS) == 0) {
tinfo->exe_writable = true;
}
}
if(thread_seteuid(orig_uid) == -1)
{
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Could not restore original euid from %d to %d",
uid, orig_uid);
return SCAP_FAILURE;
}
if(thread_setegid(orig_gid) == -1)
{
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Could not restore original egid from %d to %d",
gid, orig_gid);
return SCAP_FAILURE;
}
return SCAP_SUCCESS;
}
// //
// Add a process to the list by parsing its entry under /proc // Add a process to the list by parsing its entry under /proc
// //
@ -860,6 +922,14 @@ static int32_t scap_proc_add_from_proc(scap_t* handle, uint32_t tid, char* procd
tinfo->flags = PPM_CL_CLONE_THREAD | PPM_CL_CLONE_FILES; tinfo->flags = PPM_CL_CLONE_THREAD | PPM_CL_CLONE_FILES;
} }
if(SCAP_FAILURE == scap_proc_fill_exe_writable(handle, tinfo, tinfo->uid, tinfo->gid, dir_name, target_name))
{
snprintf(error, SCAP_LASTERR_SIZE, "can't fill exe writable access for %s (%s)",
dir_name, handle->m_lasterr);
free(tinfo);
return SCAP_FAILURE;
}
// //
// if procinfo is set we assume this is a runtime lookup so no // if procinfo is set we assume this is a runtime lookup so no
// need to use the table // need to use the table
@ -1449,6 +1519,7 @@ int32_t scap_check_suppressed(scap_t *handle, scap_evt *pevent, bool *suppressed
case PPME_SYSCALL_FORK_20_X: case PPME_SYSCALL_FORK_20_X:
case PPME_SYSCALL_VFORK_20_X: case PPME_SYSCALL_VFORK_20_X:
case PPME_SYSCALL_EXECVE_19_X: case PPME_SYSCALL_EXECVE_19_X:
case PPME_SYSCALL_EXECVEAT_X:
lens = (uint16_t *)((char *)pevent + sizeof(struct ppm_evt_hdr)); lens = (uint16_t *)((char *)pevent + sizeof(struct ppm_evt_hdr));
valptr = (char *)lens + pevent->nparams * sizeof(uint16_t); valptr = (char *)lens + pevent->nparams * sizeof(uint16_t);

File diff suppressed because it is too large Load Diff

View File

@ -72,7 +72,7 @@ typedef struct _section_header_block
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#define MI_BLOCK_TYPE 0x201 #define MI_BLOCK_TYPE 0x201
#define MI_BLOCK_TYPE_INT 0x8002ABCD // This is the unofficial number used before the #define MI_BLOCK_TYPE_INT 0x8002ABCD // This is the unofficial number used before the
// library release. We'll keep him for a while for // library release. We'll keep it for a while for
// backward compatibility // backward compatibility
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -80,17 +80,17 @@ typedef struct _section_header_block
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#define PL_BLOCK_TYPE_V1 0x202 #define PL_BLOCK_TYPE_V1 0x202
#define PL_BLOCK_TYPE_V1_INT 0x8000ABCD // This is the unofficial number used before the #define PL_BLOCK_TYPE_V1_INT 0x8000ABCD // This is the unofficial number used before the
// library release. We'll keep him for a while for // library release. We'll keep it for a while for
// backward compatibility // backward compatibility
#define PL_BLOCK_TYPE_V2 0x207 #define PL_BLOCK_TYPE_V2 0x207
#define PL_BLOCK_TYPE_V2_INT 0x8013ABCD // This is the unofficial number used before the #define PL_BLOCK_TYPE_V2_INT 0x8013ABCD // This is the unofficial number used before the
// library release. We'll keep him for a while for // library release. We'll keep it for a while for
// backward compatibility // backward compatibility
#define PL_BLOCK_TYPE_V3 0x209 #define PL_BLOCK_TYPE_V3 0x209
#define PL_BLOCK_TYPE_V3_INT 0x8014ABCD // This is the unofficial number used before the #define PL_BLOCK_TYPE_V3_INT 0x8014ABCD // This is the unofficial number used before the
// library release. We'll keep him for a while for // library release. We'll keep it for a while for
// backward compatibility // backward compatibility
#define PL_BLOCK_TYPE_V4 0x210 #define PL_BLOCK_TYPE_V4 0x210
@ -110,7 +110,7 @@ typedef struct _section_header_block
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#define FDL_BLOCK_TYPE 0x203 #define FDL_BLOCK_TYPE 0x203
#define FDL_BLOCK_TYPE_INT 0x8001ABCD // This is the unofficial number used before the #define FDL_BLOCK_TYPE_INT 0x8001ABCD // This is the unofficial number used before the
// library release. We'll keep him for a while for // library release. We'll keep it for a while for
// backward compatibility // backward compatibility
#define FDL_BLOCK_TYPE_V2 0x218 #define FDL_BLOCK_TYPE_V2 0x218
@ -119,16 +119,18 @@ typedef struct _section_header_block
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#define EV_BLOCK_TYPE 0x204 #define EV_BLOCK_TYPE 0x204
#define EV_BLOCK_TYPE_INT 0x8010ABCD // This is the unofficial number used before the #define EV_BLOCK_TYPE_INT 0x8010ABCD // This is the unofficial number used before the
// library release. We'll keep him for a while for // library release. We'll keep it for a while for
// backward compatibility // backward compatibility
#define EV_BLOCK_TYPE_V2 0x216 #define EV_BLOCK_TYPE_V2 0x216
#define EV_BLOCK_TYPE_V2_LARGE 0x221
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// INTERFACE LIST BLOCK // INTERFACE LIST BLOCK
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#define IL_BLOCK_TYPE 0x205 #define IL_BLOCK_TYPE 0x205
#define IL_BLOCK_TYPE_INT 0x8011ABCD // This is the unofficial number used before the #define IL_BLOCK_TYPE_INT 0x8011ABCD // This is the unofficial number used before the
// library release. We'll keep him for a while for // library release. We'll keep it for a while for
// backward compatibility // backward compatibility
#define IL_BLOCK_TYPE_V2 0x219 #define IL_BLOCK_TYPE_V2 0x219
@ -137,7 +139,7 @@ typedef struct _section_header_block
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#define UL_BLOCK_TYPE 0x206 #define UL_BLOCK_TYPE 0x206
#define UL_BLOCK_TYPE_INT 0x8012ABCD // This is the unofficial number used before the #define UL_BLOCK_TYPE_INT 0x8012ABCD // This is the unofficial number used before the
// library release. We'll keep him for a while for // library release. We'll keep it for a while for
// backward compatibility // backward compatibility
#define UL_BLOCK_TYPE_V2 0x220 #define UL_BLOCK_TYPE_V2 0x220
@ -148,6 +150,8 @@ typedef struct _section_header_block
#define EVF_BLOCK_TYPE_V2 0x217 #define EVF_BLOCK_TYPE_V2 0x217
#define EVF_BLOCK_TYPE_V2_LARGE 0x222
#if defined __sun #if defined __sun
#pragma pack() #pragma pack()
#else #else

View File

@ -18,6 +18,7 @@ limitations under the License.
#include <stdio.h> #include <stdio.h>
#include "scap.h" #include "scap.h"
#include "scap-int.h" #include "scap-int.h"
#include "../common/strlcpy.h"
#if defined(HAS_CAPTURE) && !defined(_WIN32) #if defined(HAS_CAPTURE) && !defined(_WIN32)
#include <sys/types.h> #include <sys/types.h>
@ -30,8 +31,8 @@ limitations under the License.
// //
int32_t scap_create_userlist(scap_t* handle) int32_t scap_create_userlist(scap_t* handle)
{ {
uint32_t usercnt; uint32_t usercnt, useridx;
uint32_t grpcnt; uint32_t grpcnt, grpidx;
struct passwd *p; struct passwd *p;
struct group *g; struct group *g;
@ -45,19 +46,6 @@ int32_t scap_create_userlist(scap_t* handle)
handle->m_userlist = NULL; handle->m_userlist = NULL;
} }
//
// First pass: count the number of users and the number of groups
//
setpwent();
p = getpwent();
for(usercnt = 0; p; p = getpwent(), usercnt++);
endpwent();
setgrent();
g = getgrent();
for(grpcnt = 0; g; g = getgrent(), grpcnt++);
endgrent();
// //
// Memory allocations // Memory allocations
// //
@ -68,9 +56,8 @@ int32_t scap_create_userlist(scap_t* handle)
return SCAP_FAILURE; return SCAP_FAILURE;
} }
handle->m_userlist->nusers = usercnt;
handle->m_userlist->ngroups = grpcnt;
handle->m_userlist->totsavelen = 0; handle->m_userlist->totsavelen = 0;
usercnt = 32; // initial user count; will be realloc'd if needed
handle->m_userlist->users = (scap_userinfo*)malloc(usercnt * sizeof(scap_userinfo)); handle->m_userlist->users = (scap_userinfo*)malloc(usercnt * sizeof(scap_userinfo));
if(handle->m_userlist->users == NULL) if(handle->m_userlist->users == NULL)
{ {
@ -79,6 +66,7 @@ int32_t scap_create_userlist(scap_t* handle)
return SCAP_FAILURE; return SCAP_FAILURE;
} }
grpcnt = 32; // initial group count; will be realloc'd if needed
handle->m_userlist->groups = (scap_groupinfo*)malloc(grpcnt * sizeof(scap_groupinfo)); handle->m_userlist->groups = (scap_groupinfo*)malloc(grpcnt * sizeof(scap_groupinfo));
if(handle->m_userlist->groups == NULL) if(handle->m_userlist->groups == NULL)
{ {
@ -88,82 +76,126 @@ int32_t scap_create_userlist(scap_t* handle)
return SCAP_FAILURE; return SCAP_FAILURE;
} }
//
// Second pass: copy the data
//
// users // users
setpwent(); setpwent();
p = getpwent(); p = getpwent();
for(usercnt = 0; p; p = getpwent(), usercnt++) for(useridx = 0; p; p = getpwent(), useridx++)
{ {
handle->m_userlist->users[usercnt].uid = p->pw_uid; if (useridx == usercnt)
handle->m_userlist->users[usercnt].gid = p->pw_gid;
if(p->pw_name)
{ {
strncpy(handle->m_userlist->users[usercnt].name, p->pw_name, sizeof(handle->m_userlist->users[usercnt].name)); usercnt<<=1;
void *tmp = realloc(handle->m_userlist->users, usercnt * sizeof(scap_userinfo));
if (tmp)
{
handle->m_userlist->users = tmp;
} }
else else
{ {
*handle->m_userlist->users[usercnt].name = '\0'; snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "userlist allocation failed(2)");
free(handle->m_userlist->users);
free(handle->m_userlist->groups);
free(handle->m_userlist);
return SCAP_FAILURE;
}
}
scap_userinfo *user = &handle->m_userlist->users[useridx];
user->uid = p->pw_uid;
user->gid = p->pw_gid;
if(p->pw_name)
{
strlcpy(user->name, p->pw_name, sizeof(user->name));
}
else
{
*user->name = '\0';
} }
if(p->pw_dir) if(p->pw_dir)
{ {
strncpy(handle->m_userlist->users[usercnt].homedir, p->pw_dir, sizeof(handle->m_userlist->users[usercnt].homedir)); strlcpy(user->homedir, p->pw_dir, sizeof(user->homedir));
} }
else else
{ {
*handle->m_userlist->users[usercnt].homedir = '\0'; *user->homedir = '\0';
} }
if(p->pw_shell) if(p->pw_shell)
{ {
strncpy(handle->m_userlist->users[usercnt].shell, p->pw_shell, sizeof(handle->m_userlist->users[usercnt].shell)); strlcpy(user->shell, p->pw_shell, sizeof(user->shell));
} }
else else
{ {
*handle->m_userlist->users[usercnt].shell = '\0'; *user->shell = '\0';
} }
handle->m_userlist->totsavelen += handle->m_userlist->totsavelen +=
sizeof(uint8_t) + // type sizeof(uint8_t) + // type
sizeof(uint32_t) + // uid sizeof(uint32_t) + // uid
sizeof(uint32_t) + // gid sizeof(uint32_t) + // gid
strlen(handle->m_userlist->users[usercnt].name) + 2 + strlen(user->name) + 2 +
strlen(handle->m_userlist->users[usercnt].homedir) + 2 + strlen(user->homedir) + 2 +
strlen(handle->m_userlist->users[usercnt].shell) + 2; strlen(user->shell) + 2;
} }
endpwent(); endpwent();
handle->m_userlist->nusers = useridx;
if (useridx < usercnt)
{
// Reduce array size
handle->m_userlist->users = realloc(handle->m_userlist->users, useridx * sizeof(scap_userinfo));
}
// groups // groups
setgrent(); setgrent();
g = getgrent(); g = getgrent();
for(grpcnt = 0; g; g = getgrent(), grpcnt++) for(grpidx = 0; g; g = getgrent(), grpidx++)
{ {
handle->m_userlist->groups[grpcnt].gid = g->gr_gid; if (grpidx == grpcnt)
if(g->gr_name)
{ {
strncpy(handle->m_userlist->groups[grpcnt].name, g->gr_name, sizeof(handle->m_userlist->groups[grpcnt].name)); grpcnt<<=1;
void *tmp = realloc(handle->m_userlist->groups, grpcnt * sizeof(scap_groupinfo));
if (tmp)
{
handle->m_userlist->groups = tmp;
} }
else else
{ {
*handle->m_userlist->groups[grpcnt].name = '\0'; snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "grouplist allocation failed(2)");
free(handle->m_userlist->users);
free(handle->m_userlist->groups);
free(handle->m_userlist);
return SCAP_FAILURE;
}
}
scap_groupinfo *group = &handle->m_userlist->groups[grpidx];
group->gid = g->gr_gid;
if(g->gr_name)
{
strlcpy(group->name, g->gr_name, sizeof(group->name));
}
else
{
*group->name = '\0';
} }
handle->m_userlist->totsavelen += handle->m_userlist->totsavelen +=
sizeof(uint8_t) + // type sizeof(uint8_t) + // type
sizeof(uint32_t) + // gid sizeof(uint32_t) + // gid
strlen(handle->m_userlist->groups[grpcnt].name) + 2; strlen(group->name) + 2;
} }
endgrent(); endgrent();
handle->m_userlist->ngroups = grpidx;
if (grpidx < grpcnt)
{
// Reduce array size
handle->m_userlist->groups = realloc(handle->m_userlist->groups, grpidx * sizeof(scap_groupinfo));
}
return SCAP_SUCCESS; return SCAP_SUCCESS;
} }
#else // HAS_CAPTURE #else // HAS_CAPTURE

View File

@ -351,6 +351,10 @@ const struct ppm_syscall_desc g_syscall_info_table[PPM_SC_MAX] = {
/*PPM_SC_FADVISE64*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "fadvise64" }, /*PPM_SC_FADVISE64*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "fadvise64" },
/*PPM_SC_RENAMEAT2*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "renameat2" }, /*PPM_SC_RENAMEAT2*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "renameat2" },
/*PPM_SC_USERFAULTFD*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "userfaultfd" }, /*PPM_SC_USERFAULTFD*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "userfaultfd" },
/*PPM_SC_OPENAT2*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "openat2" },
/*PPM_SC_UMOUNT2*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "umount2" },
/*PPM_SC_EXECVE*/ { EC_SYSTEM, (enum ppm_event_flags)(EF_NONE), "execve" },
/*PPM_SC_EXECVEAT*/ { EC_SYSTEM, (enum ppm_event_flags)(EF_NONE), "execveat" },
}; };
bool validate_info_table_size() bool validate_info_table_size()

View File

@ -0,0 +1,69 @@
/*
Copyright (C) 2021 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <sys/types.h>
#include <errno.h>
#include <unistd.h>
#pragma once
/*!
\brief Set the Effective User ID only for the current thread
\return On success, zero is returned. On error, -1 is returned, and
errno is set to indicate the error.
*/
static inline int thread_seteuid(uid_t uid)
{
int result;
if (uid == (uid_t) ~0) {
errno = EINVAL;
return -1;
}
#ifdef __NR_setresuid32
result = syscall(SYS_setresuid32, -1, uid, -1);
#else
result = syscall(SYS_setresuid, -1, uid, -1);
#endif
return result;
}
/*!
\brief Set the Effective Group ID only for the current thread
\return On success, zero is returned. On error, -1 is returned, and
errno is set to indicate the error.
*/
static inline int thread_setegid(gid_t gid)
{
int result;
if (gid == (gid_t) ~0) {
errno = EINVAL;
return -1;
}
#ifdef __NR_setresgid32
result = syscall(SYS_setresgid32, -1, gid, -1);
#else
result = syscall(SYS_setresgid, -1, gid, -1);
#endif
return result;
}

View File

@ -66,6 +66,7 @@ set(SINSP_SOURCES
filter.cpp filter.cpp
fields_info.cpp fields_info.cpp
filterchecks.cpp filterchecks.cpp
filter_check_list.cpp
gen_filter.cpp gen_filter.cpp
http_parser.c http_parser.c
http_reason.cpp http_reason.cpp
@ -78,6 +79,7 @@ set(SINSP_SOURCES
"${JSONCPP_LIB_SRC}" "${JSONCPP_LIB_SRC}"
logger.cpp logger.cpp
parsers.cpp parsers.cpp
plugin.cpp
prefix_search.cpp prefix_search.cpp
protodecoder.cpp protodecoder.cpp
threadinfo.cpp threadinfo.cpp
@ -137,13 +139,16 @@ if(NOT MINIMAL_BUILD)
mesos_http.cpp mesos_http.cpp
mesos_state.cpp mesos_state.cpp
sinsp_curl.cpp sinsp_curl.cpp
container_engine/docker_common.cpp) container_engine/docker/async_source.cpp
container_engine/docker/base.cpp)
if(WIN32) if(WIN32)
list(APPEND SINSP_SOURCES list(APPEND SINSP_SOURCES
container_engine/docker_win.cpp) container_engine/docker/connection_win.cpp
container_engine/docker/docker_win.cpp)
else() else()
list(APPEND SINSP_SOURCES list(APPEND SINSP_SOURCES
container_engine/docker_linux.cpp container_engine/docker/docker_linux.cpp
container_engine/docker/connection_linux.cpp
container_engine/libvirt_lxc.cpp container_engine/libvirt_lxc.cpp
container_engine/lxc.cpp container_engine/lxc.cpp
container_engine/mesos.cpp container_engine/mesos.cpp
@ -172,6 +177,8 @@ set(SINSP_LIBRARIES
"${JSONCPP_LIB}" "${JSONCPP_LIB}"
"${CARES_LIB}") "${CARES_LIB}")
add_dependencies(sinsp valijson)
if(WITH_CHISEL AND USE_BUNDLED_LUAJIT) if(WITH_CHISEL AND USE_BUNDLED_LUAJIT)
add_dependencies(sinsp luajit) add_dependencies(sinsp luajit)
endif() endif()
@ -244,14 +251,8 @@ if(NOT WIN32)
endif() endif()
endif() # NOT APPLE endif() # NOT APPLE
if(USE_BUNDLED_OPENSSL AND NOT MINIMAL_BUILD)
list(APPEND SINSP_LIBRARIES
"${OPENSSL_LIBRARY_SSL}"
"${OPENSSL_LIBRARY_CRYPTO}")
else()
list(APPEND SINSP_LIBRARIES list(APPEND SINSP_LIBRARIES
"${OPENSSL_LIBRARIES}") "${OPENSSL_LIBRARIES}")
endif()
if(WITH_CHISEL) if(WITH_CHISEL)
list(APPEND SINSP_LIBRARIES list(APPEND SINSP_LIBRARIES

View File

@ -21,7 +21,11 @@ limitations under the License.
#ifdef HAS_CAPTURE #ifdef HAS_CAPTURE
#include "container_engine/cri.h" #include "container_engine/cri.h"
#endif // HAS_CAPTURE #endif // HAS_CAPTURE
#include "container_engine/docker.h" #ifdef _WIN32
#include "container_engine/docker/docker_win.h"
#else
#include "container_engine/docker/docker_linux.h"
#endif
#include "container_engine/rkt.h" #include "container_engine/rkt.h"
#include "container_engine/libvirt_lxc.h" #include "container_engine/libvirt_lxc.h"
#include "container_engine/lxc.h" #include "container_engine/lxc.h"
@ -245,7 +249,7 @@ string sinsp_container_manager::container_to_json(const sinsp_container_info& co
bool sinsp_container_manager::container_to_sinsp_event(const string& json, sinsp_evt* evt, shared_ptr<sinsp_threadinfo> tinfo) bool sinsp_container_manager::container_to_sinsp_event(const string& json, sinsp_evt* evt, shared_ptr<sinsp_threadinfo> tinfo)
{ {
size_t totlen = sizeof(scap_evt) + sizeof(uint16_t) + json.length() + 1; size_t totlen = sizeof(scap_evt) + sizeof(uint32_t) + json.length() + 1;
ASSERT(evt->m_pevt_storage == nullptr); ASSERT(evt->m_pevt_storage == nullptr);
evt->m_pevt_storage = new char[totlen]; evt->m_pevt_storage = new char[totlen];
@ -270,13 +274,13 @@ bool sinsp_container_manager::container_to_sinsp_event(const string& json, sinsp
} }
scapevt->tid = -1; scapevt->tid = -1;
scapevt->len = (uint32_t)totlen; scapevt->len = (uint32_t)totlen;
scapevt->type = PPME_CONTAINER_JSON_E; scapevt->type = PPME_CONTAINER_JSON_2_E;
scapevt->nparams = 1; scapevt->nparams = 1;
uint16_t* lens = (uint16_t*)((char *)scapevt + sizeof(struct ppm_evt_hdr)); uint32_t* lens = (uint32_t*)((char *)scapevt + sizeof(struct ppm_evt_hdr));
char* valptr = (char*)lens + sizeof(uint16_t); char* valptr = (char*)lens + sizeof(uint32_t);
*lens = (uint16_t)json.length() + 1; *lens = (uint32_t)json.length() + 1;
memcpy(valptr, json.c_str(), *lens); memcpy(valptr, json.c_str(), *lens);
evt->init(); evt->init();
@ -345,7 +349,7 @@ void sinsp_container_manager::dump_containers(scap_dumper_t* dumper)
sinsp_evt evt; sinsp_evt evt;
if(container_to_sinsp_event(container_to_json(*it.second), &evt, it.second->get_tinfo(m_inspector))) if(container_to_sinsp_event(container_to_json(*it.second), &evt, it.second->get_tinfo(m_inspector)))
{ {
int32_t res = scap_dump(m_inspector->m_h, dumper, evt.m_pevt, evt.m_cpuid, 0); int32_t res = scap_dump(m_inspector->m_h, dumper, evt.m_pevt, evt.m_cpuid, SCAP_DF_LARGE);
if(res != SCAP_SUCCESS) if(res != SCAP_SUCCESS)
{ {
throw sinsp_exception(scap_getlasterr(m_inspector->m_h)); throw sinsp_exception(scap_getlasterr(m_inspector->m_h));
@ -524,14 +528,14 @@ void sinsp_container_manager::create_engines()
#ifndef MINIMAL_BUILD #ifndef MINIMAL_BUILD
#ifdef CYGWING_AGENT #ifdef CYGWING_AGENT
{ {
auto docker_engine = std::make_shared<container_engine::docker>(*this, m_inspector /*wmi source*/); auto docker_engine = std::make_shared<container_engine::docker_win>(*this, m_inspector /*wmi source*/);
m_container_engines.push_back(docker_engine); m_container_engines.push_back(docker_engine);
m_container_engine_by_type[CT_DOCKER] = docker_engine; m_container_engine_by_type[CT_DOCKER] = docker_engine;
} }
#else #else
#ifndef _WIN32 #ifndef _WIN32
{ {
auto docker_engine = std::make_shared<container_engine::docker>(*this); auto docker_engine = std::make_shared<container_engine::docker_linux>(*this);
m_container_engines.push_back(docker_engine); m_container_engines.push_back(docker_engine);
m_container_engine_by_type[CT_DOCKER] = docker_engine; m_container_engine_by_type[CT_DOCKER] = docker_engine;
} }
@ -604,8 +608,8 @@ void sinsp_container_manager::cleanup()
void sinsp_container_manager::set_docker_socket_path(std::string socket_path) void sinsp_container_manager::set_docker_socket_path(std::string socket_path)
{ {
#if !defined(MINIMAL_BUILD) && defined(HAS_CAPTURE) #if !defined(MINIMAL_BUILD) && defined(HAS_CAPTURE) && !defined(_WIN32)
libsinsp::container_engine::docker::set_docker_sock(std::move(socket_path)); libsinsp::container_engine::docker_linux::set_docker_sock(std::move(socket_path));
#endif #endif
} }

View File

@ -145,8 +145,11 @@ bool cri_async_source::parse_cri(sinsp_container_info& container, const libsinsp
{ {
container.m_container_ip = m_cri->get_container_ip(container.m_id); container.m_container_ip = m_cri->get_container_ip(container.m_id);
} }
if(container.m_imageid.empty())
{
container.m_imageid = m_cri->get_container_image_id(resp_container.image_ref()); container.m_imageid = m_cri->get_container_image_id(resp_container.image_ref());
} }
}
return true; return true;
} }

View File

@ -1,205 +0,0 @@
/*
Copyright (C) 2021 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#ifndef MINIMAL_BUILD
#ifndef _WIN32
#include <memory>
#include <string>
#include <vector>
#include <atomic>
#if !defined(_WIN32)
#include <curl/curl.h>
#include <curl/easy.h>
#include <curl/multi.h>
#endif
#include "json/json.h"
#include "async_key_value_source.h"
#include "container.h"
#include "container_info.h"
#include "container_engine/container_engine_base.h"
#include "container_engine/sinsp_container_type.h"
#include "container_engine/wmi_handle_source.h"
class sinsp;
class sinsp_threadinfo;
namespace libsinsp {
namespace container_engine {
struct docker_async_instruction
{
docker_async_instruction() :
request_rw_size(false)
{}
docker_async_instruction(const std::string container_id_value,
bool rw_size_value) :
container_id(container_id_value),
request_rw_size(rw_size_value)
{}
bool operator<(const docker_async_instruction& rhs) const
{
if(container_id < rhs.container_id)
{
return true;
}
return request_rw_size < rhs.request_rw_size;
}
bool operator==(const docker_async_instruction& rhs) const
{
return container_id == rhs.container_id &&
request_rw_size == rhs.request_rw_size;
}
std::string container_id;
bool request_rw_size;
};
class docker_async_source : public sysdig::async_key_value_source<docker_async_instruction, sinsp_container_info>
{
enum docker_response
{
RESP_OK = 0,
RESP_BAD_REQUEST = 1,
RESP_ERROR = 2
};
public:
#ifdef _WIN32
docker_async_source(uint64_t max_wait_ms, uint64_t ttl_ms, container_cache_interface *cache);
#else
docker_async_source(uint64_t max_wait_ms, uint64_t ttl_ms, container_cache_interface *cache, std::string socket_path);
#endif
virtual ~docker_async_source();
static void set_query_image_info(bool query_image_info);
protected:
void run_impl();
private:
// These 4 methods are OS-dependent and defined in docker_{linux,win}.cpp
void init_docker_conn();
void free_docker_conn();
std::string build_request(const std::string& url);
docker_response get_docker(const std::string& url, std::string &json);
bool parse_docker(const docker_async_instruction& instruction, sinsp_container_info& container);
// Look for a pod specification in this container's labels and
// if found set spec to the pod spec.
bool get_k8s_pod_spec(const Json::Value &config_obj,
Json::Value &spec);
std::string normalize_arg(const std::string &arg);
// Parse a healthcheck out of the provided healthcheck object,
// updating the container info with any healthcheck found.
void parse_healthcheck(const Json::Value &healthcheck_obj,
sinsp_container_info &container);
// Parse either a readiness or liveness probe out of the
// provided object, updating the container info with any probe
// found. Returns true if the healthcheck/livenesss/readiness
// probe info was found and could be parsed.
bool parse_liveness_readiness_probe(const Json::Value &probe_obj,
sinsp_container_info::container_health_probe::probe_type ptype,
sinsp_container_info &container);
// See if this config has a io.kubernetes.sandbox.id label
// referring to a different container. (NOTE: this is not the
// same as docker's sandbox id, which refers to networks.) If
// it does, try to copy the health checks from that container
// to the provided container_info pointer. Returns true if a
// sandbox container id was found, the corresponding container
// was found, and if the health checks could be copied from
// that container.
bool get_sandbox_liveness_readiness_probes(const Json::Value &config_obj,
sinsp_container_info &container);
// Parse all healthchecks/liveness probes/readiness probes out
// of the provided object, updating the container info as required.
void parse_health_probes(const Json::Value &config_obj,
sinsp_container_info &container);
container_cache_interface *m_cache;
std::string m_api_version;
#ifndef _WIN32
std::string m_docker_unix_socket_path;
CURLM *m_curlm;
CURL *m_curl;
#endif
static bool m_query_image_info;
};
class docker : public container_engine_base
{
public:
#ifdef _WIN32
docker(container_cache_interface &cache, const wmi_handle_source&);
#else
docker(container_cache_interface &cache) : container_engine_base(cache)
{}
#endif
void cleanup() override;
static void parse_json_mounts(const Json::Value &mnt_obj, std::vector<sinsp_container_info::container_mount_info> &mounts);
// Container name only set for windows. For linux name must be fetched via lookup
static bool detect_docker(const sinsp_threadinfo* tinfo, std::string& container_id, std::string &container_name);
#ifndef _WIN32
static void set_docker_sock(std::string docker_sock) {
m_docker_sock = std::move(docker_sock);
}
#endif
protected:
void parse_docker_async(const std::string& container_id, container_cache_interface *cache);
std::unique_ptr<docker_async_source> m_docker_info_source;
static std::string s_incomplete_info_name;
#ifdef _WIN32
const wmi_handle_source& m_wmi_handle_source;
#else
static std::string m_docker_sock;
#endif
private:
// implement container_engine_base
bool resolve(sinsp_threadinfo *tinfo, bool query_os_for_missing_info) override;
void update_with_size(const std::string& container_id) override;
};
}
}
#endif // _WIN32
#endif // MINIMAL_BUILD

View File

@ -14,10 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
#include "async_source.h"
#ifndef _WIN32
#include "container_engine/docker.h"
#include "cgroup_list_counter.h" #include "cgroup_list_counter.h"
#include "sinsp.h" #include "sinsp.h"
#include "sinsp_int.h" #include "sinsp_int.h"
@ -27,25 +24,14 @@ limitations under the License.
using namespace libsinsp::container_engine; using namespace libsinsp::container_engine;
bool docker_async_source::m_query_image_info = true;
docker_async_source::docker_async_source(uint64_t max_wait_ms, docker_async_source::docker_async_source(uint64_t max_wait_ms,
uint64_t ttl_ms, uint64_t ttl_ms,
container_cache_interface *cache container_cache_interface *cache)
#ifndef _WIN32
, std::string socket_path
#endif
)
: async_key_value_source(max_wait_ms, ttl_ms), : async_key_value_source(max_wait_ms, ttl_ms),
m_cache(cache), m_cache(cache)
#ifdef _WIN32
m_api_version("/v1.30")
#else
m_api_version("/v1.24"),
m_docker_unix_socket_path(std::move(socket_path)),
m_curlm(NULL),
m_curl(NULL)
#endif
{ {
init_docker_conn();
} }
docker_async_source::~docker_async_source() docker_async_source::~docker_async_source()
@ -53,28 +39,26 @@ docker_async_source::~docker_async_source()
this->stop(); this->stop();
g_logger.format(sinsp_logger::SEV_DEBUG, g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async: Source destructor"); "docker_async: Source destructor");
free_docker_conn();
} }
void docker_async_source::run_impl() void docker_async_source::run_impl()
{ {
docker_async_instruction instruction; docker_lookup_request request;
while (dequeue_next_key(instruction)) while (dequeue_next_key(request))
{ {
g_logger.format(sinsp_logger::SEV_DEBUG, g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s : %s): Source dequeued key", "docker_async (%s : %s): Source dequeued key",
instruction.container_id.c_str(), request.container_id.c_str(),
instruction.request_rw_size ? "true" : "false"); request.request_rw_size ? "true" : "false");
sinsp_container_info res; sinsp_container_info res;
res.m_lookup_state = sinsp_container_lookup_state::SUCCESSFUL; res.m_lookup_state = sinsp_container_lookup_state::SUCCESSFUL;
res.m_type = CT_DOCKER; res.m_type = CT_DOCKER;
res.m_id = instruction.container_id; res.m_id = request.container_id;
if(!parse_docker(instruction, res)) if(!parse_docker(request, res))
{ {
// This is not always an error e.g. when using // This is not always an error e.g. when using
// containerd as the runtime. Since the cgroup // containerd as the runtime. Since the cgroup
@ -83,33 +67,17 @@ void docker_async_source::run_impl()
// fetch both. // fetch both.
g_logger.format(sinsp_logger::SEV_DEBUG, g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s): Failed to get Docker metadata, returning successful=false", "docker_async (%s): Failed to get Docker metadata, returning successful=false",
instruction.container_id.c_str()); request.container_id.c_str());
res.m_lookup_state = sinsp_container_lookup_state::FAILED; res.m_lookup_state = sinsp_container_lookup_state::FAILED;
} }
g_logger.format(sinsp_logger::SEV_DEBUG, g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s): Parse successful, storing value", "docker_async (%s): Parse successful, storing value",
instruction.container_id.c_str()); request.container_id.c_str());
// Return a result object either way, to ensure any // Return a result object either way, to ensure any
// new container callbacks are called. // new container callbacks are called.
store_value(instruction, res); store_value(request, res);
}
}
bool docker_async_source::m_query_image_info = true;
void docker::parse_json_mounts(const Json::Value &mnt_obj, vector<sinsp_container_info::container_mount_info> &mounts)
{
if(!mnt_obj.isNull() && mnt_obj.isArray())
{
for(uint32_t i=0; i<mnt_obj.size(); i++)
{
const Json::Value &mount = mnt_obj[i];
mounts.emplace_back(mount["Source"], mount["Destination"],
mount["Mode"], mount["RW"],
mount["Propagation"]);
}
} }
} }
@ -407,179 +375,140 @@ void docker_async_source::set_query_image_info(bool query_image_info)
m_query_image_info = query_image_info; m_query_image_info = query_image_info;
} }
std::string docker::s_incomplete_info_name = "incomplete"; void docker_async_source::fetch_image_info(const docker_lookup_request& request, sinsp_container_info& container)
bool docker::resolve(sinsp_threadinfo *tinfo, bool query_os_for_missing_info)
{ {
std::string container_id, container_name;
container_cache_interface *cache = &container_cache();
if(!detect_docker(tinfo, container_id, container_name))
{
return false;
}
if(!m_docker_info_source)
{
g_logger.log("docker_async: Creating docker async source",
sinsp_logger::SEV_DEBUG);
uint64_t max_wait_ms = 10000;
#ifdef _WIN32
docker_async_source *src = new docker_async_source(docker_async_source::NO_WAIT_LOOKUP, max_wait_ms, cache);
#else
docker_async_source *src = new docker_async_source(docker_async_source::NO_WAIT_LOOKUP, max_wait_ms, cache, m_docker_sock);
#endif
m_docker_info_source.reset(src);
}
tinfo->m_container_id = container_id;
sinsp_container_info::ptr_t container_info = cache->get_container(container_id);
if(!container_info)
{
if(!query_os_for_missing_info)
{
auto container = std::make_shared<sinsp_container_info>();
container->m_type = CT_DOCKER;
container->m_id = container_id;
cache->notify_new_container(*container);
return true;
}
#ifdef HAS_CAPTURE
if(cache->should_lookup(container_id, CT_DOCKER))
{
g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s): No existing container info",
container_id.c_str());
// give docker a chance to return metadata for this container
cache->set_lookup_status(container_id, CT_DOCKER, sinsp_container_lookup_state::STARTED);
parse_docker_async(container_id, cache);
}
#endif
return false;
}
// Returning true will prevent other container engines from
// trying to resolve the container, so only return true if we
// have complete metadata.
return container_info->is_successful();
}
void docker::parse_docker_async(const string& container_id, container_cache_interface *cache)
{
auto cb = [cache](const docker_async_instruction& instruction, const sinsp_container_info& res)
{
g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s): Source callback result=%d",
instruction.container_id.c_str(),
res.m_lookup_state);
cache->notify_new_container(res);
};
sinsp_container_info result;
docker_async_instruction instruction(container_id, false /*don't request size*/);
if(m_docker_info_source->lookup(instruction, result, cb))
{
// if a previous lookup call already found the metadata, process it now
cb(instruction, result);
// This should *never* happen, as ttl is 0 (never wait)
g_logger.format(sinsp_logger::SEV_ERROR,
"docker_async (%s): Unexpected immediate return from docker_info_source.lookup()",
container_id.c_str());
}
}
bool docker_async_source::parse_docker(const docker_async_instruction& instruction, sinsp_container_info& container)
{
string json;
g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s): Looking up info for container",
instruction.container_id.c_str());
std::string request = build_request("/containers/" + instruction.container_id + "/json");
if(instruction.request_rw_size)
{
request += "?size=true";
}
docker_response resp = get_docker(request, json);
switch(resp) {
case docker_response::RESP_BAD_REQUEST:
g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s): Initial url fetch failed, trying w/o api version",
instruction.container_id.c_str());
m_api_version = "";
json = "";
resp = get_docker(build_request("/containers/" + instruction.container_id + "/json"), json);
if (resp == docker_response::RESP_OK)
{
break;
}
/* FALLTHRU */
case docker_response::RESP_ERROR:
g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s): Url fetch failed, returning false",
instruction.container_id.c_str());
return false;
case docker_response::RESP_OK:
break;
}
g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s): Parsing containers response \"%s\"",
instruction.container_id.c_str(),
json.c_str());
Json::Value root;
Json::Reader reader; Json::Reader reader;
bool parsingSuccessful = reader.parse(json, root);
if(!parsingSuccessful) g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s) image (%s): Fetching image info",
request.container_id.c_str(),
container.m_imageid.c_str());
std::string img_json;
std::string url = "/images/" + container.m_imageid + "/json?digests=1";
g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async url: %s",
url.c_str());
if(!(m_connection.get_docker(request, url, img_json) == docker_connection::RESP_OK))
{ {
g_logger.format(sinsp_logger::SEV_ERROR, g_logger.format(sinsp_logger::SEV_ERROR,
"docker_async (%s): Could not parse json \"%s\", returning false", "docker_async (%s) image (%s): Could not fetch image info",
instruction.container_id.c_str(), request.container_id.c_str(),
json.c_str()); container.m_imageid.c_str());
return;
ASSERT(false);
return false;
} }
const Json::Value& config_obj = root["Config"]; g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s) image (%s): Image info fetch returned \"%s\"",
request.container_id.c_str(),
container.m_imageid.c_str(),
img_json.c_str());
container.m_image = config_obj["Image"].asString(); Json::Value img_root;
if(!reader.parse(img_json, img_root))
{
g_logger.format(sinsp_logger::SEV_ERROR,
"docker_async (%s) image (%s): Could not parse json image info \"%s\"",
request.container_id.c_str(),
container.m_imageid.c_str(),
img_json.c_str());
return;
}
string imgstr = root["Image"].asString(); parse_image_info(container, img_root);
size_t cpos = imgstr.find(":"); }
if(cpos != string::npos)
void docker_async_source::parse_image_info(sinsp_container_info& container, const Json::Value& img)
{
// img_root["RepoDigests"] contains only digests for images pulled from registries.
// If an image gets retagged and is never pushed to any registry, we will not find
// that entry in container.m_imagerepo. Also, for locally built images we have the
// same issue. This leads to container.m_imagedigest being empty as well.
//
// Each individual digest looks like e.g.
// "docker.io/library/redis@sha256:b6a9fc3535388a6fc04f3bdb83fb4d9d0b4ffd85e7609a6ff2f0f731427823e3"
// so we need to split it at the `@` (the part before is the repo,
// the part after is the digest)
std::unordered_set<std::string> imageDigestSet;
for(const auto& rdig : img["RepoDigests"])
{
if(rdig.isString())
{
std::string repodigest = rdig.asString();
std::string digest = repodigest.substr(repodigest.find('@')+1);
imageDigestSet.insert(digest);
if(container.m_imagerepo.empty())
{
container.m_imagerepo = repodigest.substr(0, repodigest.find('@'));
}
if(repodigest.find(container.m_imagerepo) != std::string::npos)
{
container.m_imagedigest = digest;
break;
}
}
}
// fix image digest for locally tagged images or multiple repo digests.
// Case 1: One repo digest with many tags.
// Case 2: Many repo digests with the same digest value.
if(container.m_imagedigest.empty() && imageDigestSet.size() == 1) {
container.m_imagedigest = *imageDigestSet.begin();
}
for(const auto& rtag : img["RepoTags"])
{
if(rtag.isString())
{
std::string repotag = rtag.asString();
if(container.m_imagerepo.empty())
{
container.m_imagerepo = repotag.substr(0, repotag.rfind(':'));
}
if(repotag.find(container.m_imagerepo) != std::string::npos)
{
container.m_imagetag = repotag.substr(repotag.rfind(':')+1);
break;
}
}
}
}
void docker_async_source::get_image_info(const docker_lookup_request& request, sinsp_container_info& container, const Json::Value& root)
{
container.m_image = root["Config"]["Image"].asString();
std::string imgstr = root["Image"].asString();
size_t cpos = imgstr.find(':');
if(cpos != std::string::npos)
{ {
container.m_imageid = imgstr.substr(cpos + 1); container.m_imageid = imgstr.substr(cpos + 1);
} }
parse_health_probes(config_obj, container);
// containers can be spawned using just the imageID as image name, // containers can be spawned using just the imageID as image name,
// with or without the hash prefix (e.g. sha256:) // with or without the hash prefix (e.g. sha256:)
bool no_name = !container.m_imageid.empty() && //
strncmp(container.m_image.c_str(), container.m_imageid.c_str(), // e.g. an image with the id `sha256:ddcca4b8a6f0367b5de2764dfe76b0a4bfa6d75237932185923705da47004347`
MIN(container.m_image.length(), container.m_imageid.length())) == 0; // can be used to run a container as:
no_name |= !imgstr.empty() && // - docker run sha256:ddcca4b8a6f0367b5de2764dfe76b0a4bfa6d75237932185923705da47004347
strncmp(container.m_image.c_str(), imgstr.c_str(), // - docker run ddcca4b8a6f0367b5de2764dfe76b0a4bfa6d75237932185923705da47004347
MIN(container.m_image.length(), imgstr.length())) == 0; // - docker run sha256:ddcca4
// - docker run ddcca4
//
// in all these cases we need to determine the image repo/tag
// via the API (in `fetch_image_info()`)
//
// otherwise we assume the name passed to `docker run`
// (available in container.m_image) is the repo name like `redis`
// and use that to determine the name and tag
bool no_name = sinsp_utils::startswith(container.m_imageid, container.m_image) ||
sinsp_utils::startswith(imgstr, container.m_image);
if(!no_name || !m_query_image_info) if(!no_name || !m_query_image_info)
{ {
string hostname, port; std::string hostname, port;
sinsp_utils::split_container_image(container.m_image, sinsp_utils::split_container_image(container.m_image,
hostname, hostname,
port, port,
@ -590,99 +519,95 @@ bool docker_async_source::parse_docker(const docker_async_instruction& instructi
} }
if(m_query_image_info && !container.m_imageid.empty() && if(m_query_image_info && !container.m_imageid.empty() &&
(no_name || container.m_imagedigest.empty() || (!container.m_imagedigest.empty() && container.m_imagetag.empty()))) (no_name || container.m_imagedigest.empty() || container.m_imagetag.empty()))
{ {
g_logger.format(sinsp_logger::SEV_DEBUG, fetch_image_info(request, container);
"docker_async (%s) image (%s): Fetching image info",
instruction.container_id.c_str(),
container.m_imageid.c_str());
string img_json;
std::string url = "/images/" + container.m_imageid + "/json?digests=1";
g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async url: %s",
url.c_str());
if(get_docker(build_request(url), img_json) == docker_response::RESP_OK)
{
g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s) image (%s): Image info fetch returned \"%s\"",
instruction.container_id.c_str(),
container.m_imageid.c_str(),
img_json.c_str());
Json::Value img_root;
if(reader.parse(img_json, img_root))
{
// img_root["RepoDigests"] contains only digests for images pulled from registries.
// If an image gets retagged and is never pushed to any registry, we will not find
// that entry in container.m_imagerepo. Also, for locally built images we have the
// same issue. This leads to container.m_imagedigest being empty as well.
unordered_set<std::string> imageDigestSet;
for(const auto& rdig : img_root["RepoDigests"])
{
if(rdig.isString())
{
string repodigest = rdig.asString();
string digest = repodigest.substr(repodigest.find('@')+1);
imageDigestSet.insert(digest);
if(container.m_imagerepo.empty())
{
container.m_imagerepo = repodigest.substr(0, repodigest.find('@'));
}
if(repodigest.find(container.m_imagerepo) != string::npos)
{
container.m_imagedigest = digest;
break;
}
}
}
for(const auto& rtag : img_root["RepoTags"])
{
if(rtag.isString())
{
string repotag = rtag.asString();
if(container.m_imagerepo.empty())
{
container.m_imagerepo = repotag.substr(0, repotag.rfind(":"));
}
if(repotag.find(container.m_imagerepo) != string::npos)
{
container.m_imagetag = repotag.substr(repotag.rfind(":")+1);
break;
}
}
}
// fix image digest for locally tagged images or multiple repo digests.
// Case 1: One repo digest with many tags.
// Case 2: Many repo digests with the same digest value.
if(container.m_imagedigest.empty() && imageDigestSet.size() == 1) {
container.m_imagedigest = *imageDigestSet.begin();
}
}
else
{
g_logger.format(sinsp_logger::SEV_ERROR,
"docker_async (%s) image (%s): Could not parse json image info \"%s\"",
instruction.container_id.c_str(),
container.m_imageid.c_str(),
img_json.c_str());
}
}
else
{
g_logger.format(sinsp_logger::SEV_ERROR,
"docker_async (%s) image (%s): Could not fetch image info",
instruction.container_id.c_str(),
container.m_imageid.c_str());
} }
}
if(container.m_imagetag.empty()) if(container.m_imagetag.empty())
{ {
container.m_imagetag = "latest"; container.m_imagetag = "latest";
} }
}
void docker_async_source::parse_json_mounts(const Json::Value &mnt_obj, vector<sinsp_container_info::container_mount_info> &mounts)
{
if(!mnt_obj.isNull() && mnt_obj.isArray())
{
for(uint32_t i=0; i<mnt_obj.size(); i++)
{
const Json::Value &mount = mnt_obj[i];
mounts.emplace_back(mount["Source"], mount["Destination"],
mount["Mode"], mount["RW"],
mount["Propagation"]);
}
}
}
bool docker_async_source::parse_docker(const docker_lookup_request& request, sinsp_container_info& container)
{
string json;
g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s): Looking up info for container",
request.container_id.c_str());
std::string api_request = "/containers/" + request.container_id + "/json";
if(request.request_rw_size)
{
api_request += "?size=true";
}
docker_connection::docker_response resp = m_connection.get_docker(request, api_request, json);
switch(resp) {
case docker_connection::docker_response::RESP_BAD_REQUEST:
g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s): Initial url fetch failed, trying w/o api version",
request.container_id.c_str());
m_connection.set_api_version("");
json = "";
resp = m_connection.get_docker(request, "/containers/" + request.container_id + "/json", json);
if (resp == docker_connection::docker_response::RESP_OK)
{
break;
}
/* FALLTHRU */
case docker_connection::docker_response::RESP_ERROR:
g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s): Url fetch failed, returning false",
request.container_id.c_str());
return false;
case docker_connection::docker_response::RESP_OK:
break;
}
g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s): Parsing containers response \"%s\"",
request.container_id.c_str(),
json.c_str());
Json::Value root;
Json::Reader reader;
bool parsingSuccessful = reader.parse(json, root);
if(!parsingSuccessful)
{
g_logger.format(sinsp_logger::SEV_ERROR,
"docker_async (%s): Could not parse json \"%s\", returning false",
request.container_id.c_str(),
json.c_str());
ASSERT(false);
return false;
}
get_image_info(request, container, root);
const Json::Value& config_obj = root["Config"];
parse_health_probes(config_obj, container);
container.m_full_id = root["Id"].asString(); container.m_full_id = root["Id"].asString();
container.m_name = root["Name"].asString(); container.m_name = root["Name"].asString();
@ -721,16 +646,17 @@ bool docker_async_source::parse_docker(const docker_async_instruction& instructi
// separate thread so this is ok. // separate thread so this is ok.
g_logger.format(sinsp_logger::SEV_DEBUG, g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s), secondary (%s): Doing blocking fetch of secondary container", "docker_async (%s), secondary (%s): Doing blocking fetch of secondary container",
instruction.container_id.c_str(), request.container_id.c_str(),
secondary_container_id.c_str()); secondary_container_id.c_str());
if(parse_docker(docker_async_instruction(secondary_container_id, if(parse_docker(docker_lookup_request(secondary_container_id,
request.docker_socket,
false /*don't request size since we just need the IP*/), false /*don't request size since we just need the IP*/),
pcnt)) pcnt))
{ {
g_logger.format(sinsp_logger::SEV_DEBUG, g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s), secondary (%s): Secondary fetch successful", "docker_async (%s), secondary (%s): Secondary fetch successful",
instruction.container_id.c_str(), request.container_id.c_str(),
secondary_container_id.c_str()); secondary_container_id.c_str());
container.m_container_ip = pcnt.m_container_ip; container.m_container_ip = pcnt.m_container_ip;
} }
@ -738,7 +664,7 @@ bool docker_async_source::parse_docker(const docker_async_instruction& instructi
{ {
g_logger.format(sinsp_logger::SEV_ERROR, g_logger.format(sinsp_logger::SEV_ERROR,
"docker_async (%s), secondary (%s): Secondary fetch failed", "docker_async (%s), secondary (%s): Secondary fetch failed",
instruction.container_id.c_str(), request.container_id.c_str(),
secondary_container_id.c_str()); secondary_container_id.c_str());
} }
} }
@ -854,7 +780,7 @@ bool docker_async_source::parse_docker(const docker_async_instruction& instructi
container.m_privileged = privileged.asBool(); container.m_privileged = privileged.asBool();
} }
docker::parse_json_mounts(root["Mounts"], container.m_mounts); parse_json_mounts(root["Mounts"], container.m_mounts);
container.m_size_rw_bytes = root["SizeRw"].asInt64(); container.m_size_rw_bytes = root["SizeRw"].asInt64();
@ -865,29 +791,7 @@ bool docker_async_source::parse_docker(const docker_async_instruction& instructi
g_logger.format(sinsp_logger::SEV_DEBUG, g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s): parse_docker returning true", "docker_async (%s): parse_docker returning true",
instruction.container_id.c_str()); request.container_id.c_str());
return true; return true;
} }
void docker::update_with_size(const std::string &container_id)
{
auto cb = [this](const docker_async_instruction& instruction, const sinsp_container_info& res) {
g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s): with size callback result=%d",
instruction.container_id.c_str(),
res.m_lookup_state);
sinsp_container_info::ptr_t updated = make_shared<sinsp_container_info>(res);
container_cache().replace_container(updated);
};
g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async size request (%s)",
container_id.c_str());
sinsp_container_info result;
docker_async_instruction instruction(container_id, true /*request rw size*/);
(void)m_docker_info_source->lookup(instruction, result, cb);
}
#endif // _WIN32

View File

@ -0,0 +1,84 @@
#pragma once
#include "async_key_value_source.h"
#include "container_info.h"
#include "container_engine/docker/connection.h"
#include "container_engine/docker/lookup_request.h"
namespace libsinsp {
namespace container_engine {
class container_cache_interface;
class docker_async_source : public sysdig::async_key_value_source<docker_lookup_request, sinsp_container_info>
{
public:
docker_async_source(uint64_t max_wait_ms, uint64_t ttl_ms, container_cache_interface *cache);
virtual ~docker_async_source();
static void parse_json_mounts(const Json::Value &mnt_obj, std::vector<sinsp_container_info::container_mount_info> &mounts);
static void set_query_image_info(bool query_image_info);
protected:
void run_impl();
private:
bool parse_docker(const docker_lookup_request& request, sinsp_container_info& container);
// Look for a pod specification in this container's labels and
// if found set spec to the pod spec.
bool get_k8s_pod_spec(const Json::Value &config_obj,
Json::Value &spec);
std::string normalize_arg(const std::string &arg);
// Parse a healthcheck out of the provided healthcheck object,
// updating the container info with any healthcheck found.
void parse_healthcheck(const Json::Value &healthcheck_obj,
sinsp_container_info &container);
// Parse either a readiness or liveness probe out of the
// provided object, updating the container info with any probe
// found. Returns true if the healthcheck/livenesss/readiness
// probe info was found and could be parsed.
bool parse_liveness_readiness_probe(const Json::Value &probe_obj,
sinsp_container_info::container_health_probe::probe_type ptype,
sinsp_container_info &container);
// See if this config has a io.kubernetes.sandbox.id label
// referring to a different container. (NOTE: this is not the
// same as docker's sandbox id, which refers to networks.) If
// it does, try to copy the health checks from that container
// to the provided container_info pointer. Returns true if a
// sandbox container id was found, the corresponding container
// was found, and if the health checks could be copied from
// that container.
bool get_sandbox_liveness_readiness_probes(const Json::Value &config_obj,
sinsp_container_info &container);
// Parse all healthchecks/liveness probes/readiness probes out
// of the provided object, updating the container info as required.
void parse_health_probes(const Json::Value &config_obj,
sinsp_container_info &container);
// Analyze the container JSON response and get the details about
// the image, possibly executing extra API calls
void get_image_info(const docker_lookup_request& request, sinsp_container_info& container, const Json::Value& root);
// Given the image info (either the result of /images/<image-id>/json,
// or one of the items from the result of /images/json), find
// the image digest, repo and repo tag
static void parse_image_info(sinsp_container_info& container, const Json::Value& img);
// Fetch the image info for the current container's m_imageid
void fetch_image_info(const docker_lookup_request& request, sinsp_container_info& container);
container_cache_interface *m_cache;
docker_connection m_connection;
static bool m_query_image_info;
};
}
}

View File

@ -0,0 +1,86 @@
#include "base.h"
#include "sinsp.h"
using namespace libsinsp::container_engine;
void docker_base::cleanup()
{
m_docker_info_source.reset(NULL);
}
bool
docker_base::resolve_impl(sinsp_threadinfo *tinfo, const docker_lookup_request& request, bool query_os_for_missing_info)
{
container_cache_interface *cache = &container_cache();
if(!m_docker_info_source)
{
g_logger.log("docker_async: Creating docker async source",
sinsp_logger::SEV_DEBUG);
uint64_t max_wait_ms = 10000;
docker_async_source *src = new docker_async_source(docker_async_source::NO_WAIT_LOOKUP, max_wait_ms, cache);
m_docker_info_source.reset(src);
}
tinfo->m_container_id = request.container_id;
sinsp_container_info::ptr_t container_info = cache->get_container(request.container_id);
if(!container_info)
{
if(!query_os_for_missing_info)
{
auto container = std::make_shared<sinsp_container_info>();
container->m_type = CT_DOCKER;
container->m_id = request.container_id;
cache->notify_new_container(*container);
return true;
}
#ifdef HAS_CAPTURE
if(cache->should_lookup(request.container_id, CT_DOCKER))
{
g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s): No existing container info",
request.container_id.c_str());
// give docker a chance to return metadata for this container
cache->set_lookup_status(request.container_id, CT_DOCKER, sinsp_container_lookup_state::STARTED);
parse_docker_async(request, cache);
}
#endif
return false;
}
// Returning true will prevent other container engines from
// trying to resolve the container, so only return true if we
// have complete metadata.
return container_info->is_successful();
}
void docker_base::parse_docker_async(const docker_lookup_request& request, container_cache_interface *cache)
{
auto cb = [cache](const docker_lookup_request& request, const sinsp_container_info& res)
{
g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s): Source callback result=%d",
request.container_id.c_str(),
res.m_lookup_state);
cache->notify_new_container(res);
};
sinsp_container_info result;
if(m_docker_info_source->lookup(request, result, cb))
{
// if a previous lookup call already found the metadata, process it now
cb(request, result);
// This should *never* happen, as ttl is 0 (never wait)
g_logger.format(sinsp_logger::SEV_ERROR,
"docker_async (%s): Unexpected immediate return from docker_info_source.lookup()",
request.container_id.c_str());
}
}

View File

@ -0,0 +1,31 @@
#pragma once
#include "container_engine/container_engine_base.h"
#include "container_engine/docker/async_source.h"
class sinsp_threadinfo;
namespace libsinsp {
namespace container_engine {
class docker_lookup_request;
class docker_base : public container_engine_base
{
public:
docker_base(container_cache_interface &cache) : container_engine_base(cache)
{}
void cleanup() override;
protected:
void parse_docker_async(const docker_lookup_request& request, container_cache_interface *cache);
bool resolve_impl(sinsp_threadinfo *tinfo, const docker_lookup_request& request,
bool query_os_for_missing_info);
std::unique_ptr<docker_async_source> m_docker_info_source;
};
}
}

View File

@ -0,0 +1,44 @@
#pragma once
#ifndef _WIN32
#include <curl/curl.h>
#include <curl/easy.h>
#include <curl/multi.h>
#endif
#include <string>
#include "container_engine/docker/lookup_request.h"
namespace libsinsp {
namespace container_engine {
class docker_connection {
public:
enum docker_response {
RESP_OK = 0,
RESP_BAD_REQUEST = 1,
RESP_ERROR = 2
};
docker_connection();
~docker_connection();
docker_response
get_docker(const docker_lookup_request& request, const std::string& req_url, std::string& json);
void set_api_version(const std::string& api_version)
{
m_api_version = api_version;
}
private:
std::string m_api_version;
#ifndef _WIN32
CURLM *m_curlm;
#endif
};
}
}

View File

@ -14,71 +14,38 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
#include "connection.h"
#include "container_engine/docker.h"
#include "runc.h"
#include "container_engine/mesos.h"
#include "sinsp.h" #include "sinsp.h"
#include "sinsp_int.h" #include "sinsp_int.h"
using namespace libsinsp::container_engine;
using namespace libsinsp::runc;
namespace { namespace {
size_t docker_curl_write_callback(const char* ptr, size_t size, size_t nmemb, string* json) size_t docker_curl_write_callback(const char *ptr, size_t size, size_t nmemb, std::string *json)
{ {
const std::size_t total = size * nmemb; const std::size_t total = size * nmemb;
json->append(ptr, total); json->append(ptr, total);
return total; return total;
} }
constexpr const cgroup_layout DOCKER_CGROUP_LAYOUT[] = {
{"/", ""}, // non-systemd docker
{"/docker-", ".scope"}, // systemd docker
{nullptr, nullptr}
};
} }
std::string docker::m_docker_sock = "/var/run/docker.sock"; using namespace libsinsp::container_engine;
void docker::cleanup() docker_connection::docker_connection():
m_api_version("/v1.24"),
m_curlm(nullptr)
{ {
m_docker_info_source.reset(NULL);
}
void docker_async_source::init_docker_conn()
{
if(!m_curlm)
{
m_curl = curl_easy_init();
m_curlm = curl_multi_init(); m_curlm = curl_multi_init();
if(m_curlm) if(m_curlm)
{ {
curl_multi_setopt(m_curlm, CURLMOPT_PIPELINING, CURLPIPE_HTTP1|CURLPIPE_MULTIPLEX); curl_multi_setopt(m_curlm, CURLMOPT_PIPELINING, CURLPIPE_HTTP1|CURLPIPE_MULTIPLEX);
} }
if(m_curl)
{
auto docker_path = scap_get_host_root() + m_docker_unix_socket_path;
curl_easy_setopt(m_curl, CURLOPT_UNIX_SOCKET_PATH, docker_path.c_str());
curl_easy_setopt(m_curl, CURLOPT_HTTPGET, 1);
curl_easy_setopt(m_curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, docker_curl_write_callback);
}
}
} }
void docker_async_source::free_docker_conn() docker_connection::~docker_connection()
{ {
if(m_curl)
{
curl_easy_cleanup(m_curl);
m_curl = NULL;
}
if(m_curlm) if(m_curlm)
{ {
curl_multi_cleanup(m_curlm); curl_multi_cleanup(m_curlm);
@ -86,41 +53,55 @@ void docker_async_source::free_docker_conn()
} }
} }
std::string docker_async_source::build_request(const std::string &url) docker_connection::docker_response docker_connection::get_docker(const docker_lookup_request& request, const std::string& req_url, std::string &json)
{ {
return "http://localhost" + m_api_version + url; CURL* curl = curl_easy_init();
if(!curl)
{
g_logger.format(sinsp_logger::SEV_WARNING,
"docker_async (%s): Failed to initialize curl handle",
req_url.c_str());
return docker_response::RESP_ERROR;
} }
docker_async_source::docker_response docker_async_source::get_docker(const std::string& url, std::string &json) auto docker_path = scap_get_host_root() + request.docker_socket;
{ curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, docker_curl_write_callback);
curl_easy_setopt(curl, CURLOPT_UNIX_SOCKET_PATH, docker_path.c_str());
std::string url = "http://localhost" + m_api_version + req_url;
g_logger.format(sinsp_logger::SEV_DEBUG, g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s): Fetching url", "docker_async (%s): Fetching url",
url.c_str()); url.c_str());
if(curl_easy_setopt(m_curl, CURLOPT_URL, url.c_str()) != CURLE_OK) if(curl_easy_setopt(curl, CURLOPT_URL, url.c_str()) != CURLE_OK)
{ {
g_logger.format(sinsp_logger::SEV_DEBUG, g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s): curl_easy_setopt(CURLOPT_URL) failed", "docker_async (%s): curl_easy_setopt(CURLOPT_URL) failed",
url.c_str()); url.c_str());
curl_easy_cleanup(curl);
ASSERT(false); ASSERT(false);
return docker_response::RESP_ERROR; return docker_response::RESP_ERROR;
} }
if(curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &json) != CURLE_OK) if(curl_easy_setopt(curl, CURLOPT_WRITEDATA, &json) != CURLE_OK)
{ {
g_logger.format(sinsp_logger::SEV_DEBUG, g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s): curl_easy_setopt(CURLOPT_WRITEDATA) failed", "docker_async (%s): curl_easy_setopt(CURLOPT_WRITEDATA) failed",
url.c_str()); url.c_str());
curl_easy_cleanup(curl);
ASSERT(false); ASSERT(false);
return docker_response::RESP_ERROR; return docker_response::RESP_ERROR;
} }
if(curl_multi_add_handle(m_curlm, m_curl) != CURLM_OK) if(curl_multi_add_handle(m_curlm, curl) != CURLM_OK)
{ {
g_logger.format(sinsp_logger::SEV_DEBUG, g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s): curl_multi_add_handle() failed", "docker_async (%s): curl_multi_add_handle() failed",
url.c_str()); url.c_str());
curl_easy_cleanup(curl);
ASSERT(false); ASSERT(false);
return docker_response::RESP_ERROR; return docker_response::RESP_ERROR;
} }
@ -135,6 +116,8 @@ docker_async_source::docker_response docker_async_source::get_docker(const std::
"docker_async (%s): curl_multi_perform() failed", "docker_async (%s): curl_multi_perform() failed",
url.c_str()); url.c_str());
curl_multi_remove_handle(m_curlm, curl);
curl_easy_cleanup(curl);
ASSERT(false); ASSERT(false);
return docker_response::RESP_ERROR; return docker_response::RESP_ERROR;
} }
@ -151,31 +134,38 @@ docker_async_source::docker_response docker_async_source::get_docker(const std::
g_logger.format(sinsp_logger::SEV_DEBUG, g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s): curl_multi_wait() failed", "docker_async (%s): curl_multi_wait() failed",
url.c_str()); url.c_str());
curl_multi_remove_handle(m_curlm, curl);
curl_easy_cleanup(curl);
ASSERT(false); ASSERT(false);
return docker_response::RESP_ERROR; return docker_response::RESP_ERROR;
} }
} }
if(curl_multi_remove_handle(m_curlm, m_curl) != CURLM_OK) if(curl_multi_remove_handle(m_curlm, curl) != CURLM_OK)
{ {
g_logger.format(sinsp_logger::SEV_DEBUG, g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s): curl_multi_remove_handle() failed", "docker_async (%s): curl_multi_remove_handle() failed",
url.c_str()); url.c_str());
curl_easy_cleanup(curl);
ASSERT(false); ASSERT(false);
return docker_response::RESP_ERROR; return docker_response::RESP_ERROR;
} }
long http_code = 0; long http_code = 0;
if(curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &http_code) != CURLE_OK) if(curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code) != CURLE_OK)
{ {
g_logger.format(sinsp_logger::SEV_DEBUG, g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s): curl_easy_getinfo(CURLINFO_RESPONSE_CODE) failed", "docker_async (%s): curl_easy_getinfo(CURLINFO_RESPONSE_CODE) failed",
url.c_str()); url.c_str());
curl_easy_cleanup(curl);
ASSERT(false); ASSERT(false);
return docker_response::RESP_ERROR; return docker_response::RESP_ERROR;
} }
curl_easy_cleanup(curl);
g_logger.format(sinsp_logger::SEV_DEBUG, g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s): http_code=%ld", "docker_async (%s): http_code=%ld",
url.c_str(), http_code); url.c_str(), http_code);
@ -206,15 +196,3 @@ docker_async_source::docker_response docker_async_source::get_docker(const std::
return docker_response::RESP_OK; return docker_response::RESP_OK;
} }
bool docker::detect_docker(const sinsp_threadinfo *tinfo, std::string &container_id, std::string &container_name)
{
if(matches_runc_cgroups(tinfo, DOCKER_CGROUP_LAYOUT, container_id))
{
// The container name is only available in windows
container_name = s_incomplete_info_name;
return true;
}
return false;
}

View File

@ -14,58 +14,28 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
#ifdef CYGWING_AGENT #include "connection.h"
#include "container_engine/docker.h"
#include "sinsp.h"
#include "sinsp_int.h"
#include "dragent_win_hal_public.h" #include "dragent_win_hal_public.h"
using namespace libsinsp::container_engine; using namespace libsinsp::container_engine;
docker::docker(container_cache_interface& cache, const wmi_handle_source& wmi_source) : docker_connection::docker_connection():
container_engine_base(cache), m_api_version("/v1.30")
m_wmi_handle_source(wmi_source)
{ {
} }
void docker::cleanup() docker_connection::~docker_connection()
{
g_docker_info_source.reset(NULL);
}
void docker_async_source::init_docker_conn()
{ {
} }
void docker_async_source::free_docker_conn() docker_connection::docker_response docker_connection::get_docker(const docker_lookup_request& request, const std::string& req_url, std::string &json)
{ {
} std::string req = "GET " + m_api_version + req_url + " HTTP/1.1\r\nHost: docker\r\n\r\n";
std::string docker_async_source::build_request(const std::string &url)
{
return "GET " + m_api_version + url + " HTTP/1.1\r\nHost: docker\r\n\r\n";
}
bool docker::detect_docker(sinsp_threadinfo *tinfo, std::string &container_id, std::string &container_name)
{
wh_docker_container_info wcinfo = wh_docker_resolve_pid(m_wmi_handle_source.get_wmi_handle(), tinfo->m_pid);
if(!wcinfo.m_res)
{
return false;
}
container_id = wcinfo.m_container_id;
container_name = wcinfo.m_container_name;
return true;
}
docker_async_source::docker_response docker_async_source::get_docker(const std::string& url, std::string &json)
{
const char* response = NULL; const char* response = NULL;
bool qdres = wh_query_docker(m_inspector->get_wmi_handle(), bool qdres = wh_query_docker(m_inspector->get_wmi_handle(),
(char*)url.c_str(), (char*)req.c_str(),
&response); &response);
if(qdres == false) if(qdres == false)
{ {
@ -90,4 +60,3 @@ docker_async_source::docker_response docker_async_source::get_docker(const std::
return docker_response::RESP_OK; return docker_response::RESP_OK;
} }
#endif // CYGWING_AGENT

View File

@ -0,0 +1,70 @@
/*
Copyright (C) 2021 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "container_engine/docker/docker_linux.h"
#include "runc.h"
#include "sinsp_int.h"
using namespace libsinsp::container_engine;
using namespace libsinsp::runc;
namespace {
constexpr const cgroup_layout DOCKER_CGROUP_LAYOUT[] = {
{"/", ""}, // non-systemd docker
{"/docker-", ".scope"}, // systemd docker
{nullptr, nullptr}
};
}
std::string docker_linux::m_docker_sock = "/var/run/docker.sock";
bool docker_linux::resolve(sinsp_threadinfo *tinfo, bool query_os_for_missing_info)
{
std::string container_id;
if(!matches_runc_cgroups(tinfo, DOCKER_CGROUP_LAYOUT, container_id))
{
return false;
}
return resolve_impl(tinfo, docker_lookup_request(
container_id,
m_docker_sock,
false), query_os_for_missing_info);
}
void docker_linux::update_with_size(const std::string &container_id)
{
auto cb = [this](const docker_lookup_request& instruction, const sinsp_container_info& res) {
g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async (%s): with size callback result=%d",
instruction.container_id.c_str(),
res.m_lookup_state);
sinsp_container_info::ptr_t updated = make_shared<sinsp_container_info>(res);
container_cache().replace_container(updated);
};
g_logger.format(sinsp_logger::SEV_DEBUG,
"docker_async size request (%s)",
container_id.c_str());
sinsp_container_info result;
docker_lookup_request instruction(container_id, m_docker_sock, true /*request rw size*/);
(void)m_docker_info_source->lookup(instruction, result, cb);
}

View File

@ -0,0 +1,28 @@
#pragma once
#include "container_engine/container_engine_base.h"
#include "container_engine/docker/base.h"
namespace libsinsp {
namespace container_engine {
class docker_linux : public docker_base {
public:
docker_linux(container_cache_interface& cache) : docker_base(cache) {}
static void set_docker_sock(std::string docker_sock)
{
m_docker_sock = std::move(docker_sock);
}
// implement container_engine_base
bool resolve(sinsp_threadinfo *tinfo, bool query_os_for_missing_info) override;
void update_with_size(const std::string& container_id) override;
private:
static std::string m_docker_sock;
};
}
}

View File

@ -0,0 +1,53 @@
/*
Copyright (C) 2021 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifdef CYGWING_AGENT
#include "container_engine/docker.h"
#include "sinsp.h"
#include "sinsp_int.h"
#include "dragent_win_hal_public.h"
using namespace libsinsp::container_engine;
docker_win::docker_win(container_cache_interface& cache, const wmi_handle_source& wmi_source) :
container_engine_base(cache),
m_wmi_handle_source(wmi_source)
{
}
bool docker_win::resolve(sinsp_threadinfo *tinfo, bool query_os_for_missing_info)
{
wh_docker_container_info wcinfo = wh_docker_resolve_pid(m_wmi_handle_source.get_wmi_handle(), tinfo->m_pid);
if(!wcinfo.m_res)
{
return false;
}
std::string container_id = wcinfo.m_container_id;
return resolve_impl(tinfo, docker_async_instruction(
container_id,
"",
false), query_os_for_missing_info);
}
void docker_win::update_with_size(const std::string &container_id)
{
// not supported
}
#endif // CYGWING_AGENT

View File

@ -0,0 +1,24 @@
#pragma once
#include "container_engine/container_engine_base.h"
#include "container_engine/docker_base.h"
namespace libsinsp {
namespace container_engine {
class docker_win : public docker_base
{
public:
docker_win(container_cache_interface &cache, const wmi_handle_source&);
// implement container_engine_base
bool resolve(sinsp_threadinfo *tinfo, bool query_os_for_missing_info) override;
void update_with_size(const std::string& container_id) override;
private:
static std::string s_incomplete_info_name;
const wmi_handle_source& m_wmi_handle_source;
};
}
}

View File

@ -0,0 +1,49 @@
#pragma once
namespace libsinsp {
namespace container_engine {
struct docker_lookup_request
{
docker_lookup_request() :
request_rw_size(false)
{}
docker_lookup_request(const std::string& container_id_value,
const std::string& docker_socket_value,
bool rw_size_value) :
container_id(container_id_value),
docker_socket(docker_socket_value),
request_rw_size(rw_size_value)
{}
bool operator<(const docker_lookup_request& rhs) const
{
if(container_id != rhs.container_id)
{
return container_id < rhs.container_id;
}
if(docker_socket != rhs.docker_socket)
{
return docker_socket < rhs.docker_socket;
}
return request_rw_size < rhs.request_rw_size;
}
bool operator==(const docker_lookup_request& rhs) const
{
return container_id == rhs.container_id &&
docker_socket == rhs.docker_socket &&
request_rw_size == rhs.request_rw_size;
}
std::string container_id;
std::string docker_socket;
bool request_rw_size;
};
}
}

View File

@ -1,6 +1,6 @@
--- ---
layout: default layout: default
title: sysdig | libsinsp title: falcosecurity | libsinsp
--- ---
<link href="tabs.css" rel="stylesheet" type="text/css"/> <link href="tabs.css" rel="stylesheet" type="text/css"/>

View File

@ -138,7 +138,7 @@ sinsp_threadinfo* sinsp_evt::get_thread_info(bool query_os_if_not_found)
return m_tinfo; return m_tinfo;
} }
return &*m_inspector->get_thread_ref(m_pevt->tid, query_os_if_not_found, false); return m_inspector->get_thread_ref(m_pevt->tid, query_os_if_not_found, false).get();
} }
int64_t sinsp_evt::get_fd_num() int64_t sinsp_evt::get_fd_num()
@ -742,7 +742,7 @@ Json::Value sinsp_evt::get_param_as_json(uint32_t id, OUT const char** resolved_
{ {
const ppm_param_info* param_info; const ppm_param_info* param_info;
char* payload; char* payload;
uint16_t payload_len; uint32_t payload_len;
Json::Value ret; Json::Value ret;
// //
@ -834,7 +834,7 @@ Json::Value sinsp_evt::get_param_as_json(uint32_t id, OUT const char** resolved_
ASSERT(payload_len == sizeof(int64_t)); ASSERT(payload_len == sizeof(int64_t));
ret = (Json::Value::UInt64)*(int64_t *)payload; ret = (Json::Value::UInt64)*(int64_t *)payload;
sinsp_threadinfo* atinfo = &*m_inspector->get_thread_ref(*(int64_t *)payload, false, true); sinsp_threadinfo* atinfo = m_inspector->get_thread_ref(*(int64_t *)payload, false, true).get();
if(atinfo != NULL) if(atinfo != NULL)
{ {
string& tcomm = atinfo->m_comm; string& tcomm = atinfo->m_comm;
@ -1457,7 +1457,7 @@ const char* sinsp_evt::get_param_as_str(uint32_t id, OUT const char** resolved_s
const ppm_param_info* param_info; const ppm_param_info* param_info;
char* payload; char* payload;
uint32_t j; uint32_t j;
uint16_t payload_len; uint32_t payload_len;
// //
// Make sure the params are actually loaded // Make sure the params are actually loaded
@ -1553,7 +1553,7 @@ const char* sinsp_evt::get_param_as_str(uint32_t id, OUT const char** resolved_s
"%" PRId64, *(int64_t *)payload); "%" PRId64, *(int64_t *)payload);
sinsp_threadinfo* atinfo = &*m_inspector->get_thread_ref(*(int64_t *)payload, false, true); sinsp_threadinfo* atinfo = m_inspector->get_thread_ref(*(int64_t *)payload, false, true).get();
if(atinfo != NULL) if(atinfo != NULL)
{ {
string& tcomm = atinfo->m_comm; string& tcomm = atinfo->m_comm;
@ -2162,10 +2162,9 @@ const char* sinsp_evt::get_param_as_str(uint32_t id, OUT const char** resolved_s
snprintf(&m_paramstr_storage[0], snprintf(&m_paramstr_storage[0],
m_paramstr_storage.size(), m_paramstr_storage.size(),
"%d", val); "%d", val);
auto find_it = m_inspector->get_userlist()->find(val); auto user_info = m_inspector->get_user(val);
if (find_it != m_inspector->get_userlist()->end()) if (user_info != NULL)
{ {
scap_userinfo* user_info = find_it->second;
strcpy_sanitized(&m_resolved_paramstr_storage[0], user_info->name, strcpy_sanitized(&m_resolved_paramstr_storage[0], user_info->name,
(uint32_t)m_resolved_paramstr_storage.size()); (uint32_t)m_resolved_paramstr_storage.size());
} }
@ -2195,10 +2194,9 @@ const char* sinsp_evt::get_param_as_str(uint32_t id, OUT const char** resolved_s
snprintf(&m_paramstr_storage[0], snprintf(&m_paramstr_storage[0],
m_paramstr_storage.size(), m_paramstr_storage.size(),
"%d", val); "%d", val);
auto find_it = m_inspector->get_grouplist()->find(val); auto group_info = m_inspector->get_group(val);
if (find_it != m_inspector->get_grouplist()->end()) if (group_info != NULL)
{ {
scap_groupinfo* group_info = find_it->second;
strcpy_sanitized(&m_resolved_paramstr_storage[0], group_info->name, strcpy_sanitized(&m_resolved_paramstr_storage[0], group_info->name,
(uint32_t)m_resolved_paramstr_storage.size()); (uint32_t)m_resolved_paramstr_storage.size());
} }
@ -2630,6 +2628,11 @@ scap_dump_flags sinsp_evt::get_dump_flags(OUT bool* should_drop)
dflags |= SCAP_DF_TRACER; dflags |= SCAP_DF_TRACER;
} }
if(get_info_flags() & EF_LARGE_PAYLOAD)
{
dflags |= SCAP_DF_LARGE;
}
return (scap_dump_flags)dflags; return (scap_dump_flags)dflags;
} }
#endif #endif
@ -2664,7 +2667,8 @@ bool sinsp_evt::is_file_open_error() const
((m_pevt->type == PPME_SYSCALL_OPEN_X) || ((m_pevt->type == PPME_SYSCALL_OPEN_X) ||
(m_pevt->type == PPME_SYSCALL_CREAT_X) || (m_pevt->type == PPME_SYSCALL_CREAT_X) ||
(m_pevt->type == PPME_SYSCALL_OPENAT_X) || (m_pevt->type == PPME_SYSCALL_OPENAT_X) ||
(m_pevt->type == PPME_SYSCALL_OPENAT_2_X)); (m_pevt->type == PPME_SYSCALL_OPENAT_2_X) ||
(m_pevt->type == PPME_SYSCALL_OPENAT2_X));
} }
bool sinsp_evt::is_file_error() const bool sinsp_evt::is_file_error() const

View File

@ -85,9 +85,9 @@ class SINSP_PUBLIC sinsp_evt_param
{ {
public: public:
char* m_val; ///< Pointer to the event parameter data. char* m_val; ///< Pointer to the event parameter data.
uint16_t m_len; ///< Length of the parameter pointed by m_val. uint32_t m_len; ///< Length of the parameter pointed by m_val.
private: private:
inline void init(char* valptr, uint16_t len) inline void init(char* valptr, uint32_t len)
{ {
m_val = valptr; m_val = valptr;
m_len = len; m_len = len;
@ -447,16 +447,37 @@ private:
// event table entry may contain new parameters. // event table entry may contain new parameters.
// Use the minimum between the two values. // Use the minimum between the two values.
nparams = m_info->nparams < m_pevt->nparams ? m_info->nparams : m_pevt->nparams; nparams = m_info->nparams < m_pevt->nparams ? m_info->nparams : m_pevt->nparams;
uint16_t *lens = (uint16_t *)((char *)m_pevt + sizeof(struct ppm_evt_hdr));
char *valptr;
union {
uint16_t* lens16;
uint32_t* lens32;
} lens;
const bool large_payload = get_info_flags() & EF_LARGE_PAYLOAD;
if (large_payload) {
lens.lens32 = (uint32_t *)((char *)m_pevt + sizeof(struct ppm_evt_hdr));
// The offset in the block is instead always based on the capture value. // The offset in the block is instead always based on the capture value.
char *valptr = (char *)lens + m_pevt->nparams * sizeof(uint16_t); valptr = (char *)lens.lens32 + m_pevt->nparams * sizeof(uint32_t);
} else
{
lens.lens16 = (uint16_t*)((char*)m_pevt + sizeof(struct ppm_evt_hdr));
// The offset in the block is instead always based on the capture value.
valptr = (char *)lens.lens16 + m_pevt->nparams * sizeof(uint16_t);
}
m_params.clear(); m_params.clear();
for(j = 0; j < nparams; j++) for(j = 0; j < nparams; j++)
{ {
par.init(valptr, lens[j]); if (large_payload) {
par.init(valptr, lens.lens32[j]);
valptr += lens.lens32[j];
} else {
par.init(valptr, lens.lens16[j]);
valptr += lens.lens16[j];
}
m_params.push_back(par); m_params.push_back(par);
valptr += lens[j];
} }
} }
std::string get_param_value_str(uint32_t id, bool resolved); std::string get_param_value_str(uint32_t id, bool resolved);

View File

@ -25,12 +25,32 @@ limitations under the License.
// rawstring_check implementation // rawstring_check implementation
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#ifdef HAS_FILTERING #ifdef HAS_FILTERING
extern sinsp_filter_check_list g_filterlist;
sinsp_evt_formatter::sinsp_evt_formatter(sinsp* inspector, const string& fmt) sinsp_evt_formatter::sinsp_evt_formatter(sinsp* inspector,
filter_check_list &available_checks)
: m_inspector(inspector),
m_available_checks(available_checks)
{ {
m_inspector = inspector; }
set_format(fmt);
sinsp_evt_formatter::sinsp_evt_formatter(sinsp* inspector,
const string& fmt,
filter_check_list &available_checks)
: m_inspector(inspector),
m_available_checks(available_checks)
{
gen_event_formatter::output_format of = gen_event_formatter::OF_NORMAL;
if(m_inspector->get_buffer_format() == sinsp_evt::PF_JSON
|| m_inspector->get_buffer_format() == sinsp_evt::PF_JSONEOLS
|| m_inspector->get_buffer_format() == sinsp_evt::PF_JSONHEX
|| m_inspector->get_buffer_format() == sinsp_evt::PF_JSONHEXASCII
|| m_inspector->get_buffer_format() == sinsp_evt::PF_JSONBASE64)
{
of = gen_event_formatter::OF_JSON;
}
set_format(of, fmt);
} }
sinsp_evt_formatter::~sinsp_evt_formatter() sinsp_evt_formatter::~sinsp_evt_formatter()
@ -43,12 +63,14 @@ sinsp_evt_formatter::~sinsp_evt_formatter()
} }
} }
void sinsp_evt_formatter::set_format(const string& fmt) void sinsp_evt_formatter::set_format(gen_event_formatter::output_format of, const string& fmt)
{ {
uint32_t j; uint32_t j;
uint32_t last_nontoken_str_start = 0; uint32_t last_nontoken_str_start = 0;
string lfmt(fmt); string lfmt(fmt);
m_output_format = of;
if(lfmt == "") if(lfmt == "")
{ {
throw sinsp_exception("empty formatting token"); throw sinsp_exception("empty formatting token");
@ -126,7 +148,7 @@ void sinsp_evt_formatter::set_format(const string& fmt)
} }
} }
sinsp_filter_check* chk = g_filterlist.new_filter_check_from_fldname(string(cfmt + j + 1), sinsp_filter_check* chk = m_available_checks.new_filter_check_from_fldname(string(cfmt + j + 1),
m_inspector, m_inspector,
false); false);
@ -200,25 +222,33 @@ bool sinsp_evt_formatter::resolve_tokens(sinsp_evt *evt, map<string,string>& val
return retval; return retval;
} }
bool sinsp_evt_formatter::get_field_values(gen_event *gevt, std::map<std::string, std::string> &fields)
{
sinsp_evt *evt = static_cast<sinsp_evt *>(gevt);
bool sinsp_evt_formatter::tostring(sinsp_evt* evt, OUT string* res) return resolve_tokens(evt, fields);
}
gen_event_formatter::output_format sinsp_evt_formatter::get_output_format()
{
return m_output_format;
}
bool sinsp_evt_formatter::tostring_withformat(gen_event* gevt, std::string &output, gen_event_formatter::output_format of)
{ {
bool retval = true; bool retval = true;
const filtercheck_field_info* fi; const filtercheck_field_info* fi;
sinsp_evt *evt = static_cast<sinsp_evt *>(gevt);
uint32_t j = 0; uint32_t j = 0;
vector<sinsp_filter_check*>::iterator it; output.clear();
res->clear();
ASSERT(m_tokenlens.size() == m_tokens.size()); ASSERT(m_tokenlens.size() == m_tokens.size());
for(j = 0; j < m_tokens.size(); j++) for(j = 0; j < m_tokens.size(); j++)
{ {
if(m_inspector->get_buffer_format() == sinsp_evt::PF_JSON if(of == OF_JSON)
|| m_inspector->get_buffer_format() == sinsp_evt::PF_JSONEOLS
|| m_inspector->get_buffer_format() == sinsp_evt::PF_JSONHEX
|| m_inspector->get_buffer_format() == sinsp_evt::PF_JSONHEXASCII
|| m_inspector->get_buffer_format() == sinsp_evt::PF_JSONBASE64)
{ {
Json::Value json_value = m_tokens[j].second->tojson(evt); Json::Value json_value = m_tokens[j].second->tojson(evt);
@ -268,35 +298,41 @@ bool sinsp_evt_formatter::tostring(sinsp_evt* evt, OUT string* res)
{ {
string sstr(str); string sstr(str);
sstr.resize(tks, ' '); sstr.resize(tks, ' ');
(*res) += sstr; output += sstr;
} }
else else
{ {
(*res) += str; output += str;
} }
} }
} }
if(m_inspector->get_buffer_format() == sinsp_evt::PF_JSON if(of == OF_JSON)
|| m_inspector->get_buffer_format() == sinsp_evt::PF_JSONEOLS
|| m_inspector->get_buffer_format() == sinsp_evt::PF_JSONHEX
|| m_inspector->get_buffer_format() == sinsp_evt::PF_JSONHEXASCII
|| m_inspector->get_buffer_format() == sinsp_evt::PF_JSONBASE64)
{ {
(*res) = m_writer.write(m_root); output = m_writer.write(m_root);
(*res) = res->substr(0, res->size() - 1); output = output.substr(0, output.size() - 1);
} }
return retval; return retval;
} }
bool sinsp_evt_formatter::tostring(gen_event* gevt, std::string &output)
{
return tostring_withformat(gevt, output, m_output_format);
}
bool sinsp_evt_formatter::tostring(sinsp_evt* evt, OUT string* res)
{
return tostring_withformat(evt, *res, m_output_format);
}
#else // HAS_FILTERING #else // HAS_FILTERING
sinsp_evt_formatter::sinsp_evt_formatter(sinsp* inspector, const string& fmt) sinsp_evt_formatter::sinsp_evt_formatter(sinsp* inspector)
{ {
} }
void sinsp_evt_formatter::set_format(const string& fmt) void sinsp_evt_formatter::set_format(gen_event_formatter::output_format of, const std::string &format) = 0;
{ {
throw sinsp_exception("sinsp_evt_formatter unavailable because it was not compiled in the library"); throw sinsp_exception("sinsp_evt_formatter unavailable because it was not compiled in the library");
} }
@ -306,7 +342,12 @@ bool sinsp_evt_formatter::resolve_tokens(sinsp_evt *evt, map<string,string>& val
throw sinsp_exception("sinsp_evt_formatter unavailable because it was not compiled in the library"); throw sinsp_exception("sinsp_evt_formatter unavailable because it was not compiled in the library");
} }
bool sinsp_evt_formatter::tostring(sinsp_evt* evt, OUT string* res) bool sinsp_evt_formatter::tostring(gen_event* gevt, std::string &output)
{
throw sinsp_exception("sinsp_evt_formatter unavailable because it was not compiled in the library");
}
bool sinsp_evt_formatter::tostring_withformat(gen_event* gevt, std::string &output, gen_event_formatter::output_format of)
{ {
throw sinsp_exception("sinsp_evt_formatter unavailable because it was not compiled in the library"); throw sinsp_exception("sinsp_evt_formatter unavailable because it was not compiled in the library");
} }
@ -342,5 +383,42 @@ bool sinsp_evt_formatter_cache::resolve_tokens(sinsp_evt *evt, string &format, m
bool sinsp_evt_formatter_cache::tostring(sinsp_evt *evt, string &format, OUT string *res) bool sinsp_evt_formatter_cache::tostring(sinsp_evt *evt, string &format, OUT string *res)
{ {
return get_cached_formatter(format)->tostring(evt, res); return get_cached_formatter(format)->tostring(evt, *res);
}
sinsp_evt_formatter_factory::sinsp_evt_formatter_factory(sinsp *inspector, filter_check_list &available_checks)
: m_inspector(inspector),
m_available_checks(available_checks),
m_output_format(gen_event_formatter::OF_NORMAL)
{
}
sinsp_evt_formatter_factory::~sinsp_evt_formatter_factory()
{
}
void sinsp_evt_formatter_factory::set_output_format(gen_event_formatter::output_format of)
{
m_formatters.clear();
m_output_format = of;
}
std::shared_ptr<gen_event_formatter> sinsp_evt_formatter_factory::create_formatter(const std::string &format)
{
auto it = m_formatters.find(format);
if (it != m_formatters.end())
{
return it->second;
}
std::shared_ptr<gen_event_formatter> ret;
ret.reset(new sinsp_evt_formatter(m_inspector, m_available_checks));
ret->set_format(m_output_format, format);
m_formatters[format] = ret;
return ret;
} }

View File

@ -16,8 +16,14 @@ limitations under the License.
*/ */
#pragma once #pragma once
#include <map>
#include <utility>
#include <string>
#include <json/json.h> #include <json/json.h>
#include "filter_check_list.h"
#include "gen_filter.h"
class sinsp_filter_check; class sinsp_filter_check;
/** @defgroup event Event manipulation /** @defgroup event Event manipulation
@ -29,7 +35,7 @@ class sinsp_filter_check;
This class can be used to format an event into a string, based on an arbitrary This class can be used to format an event into a string, based on an arbitrary
format. format.
*/ */
class SINSP_PUBLIC sinsp_evt_formatter class SINSP_PUBLIC sinsp_evt_formatter : public gen_event_formatter
{ {
public: public:
/*! /*!
@ -41,7 +47,11 @@ public:
as the one of the sysdig '-p' command line flag, so refer to the sysdig as the one of the sysdig '-p' command line flag, so refer to the sysdig
manual for details. manual for details.
*/ */
sinsp_evt_formatter(sinsp* inspector, const string& fmt); sinsp_evt_formatter(sinsp* inspector, filter_check_list &available_checks = g_filterlist);
sinsp_evt_formatter(sinsp* inspector, const string& fmt, filter_check_list &available_checks = g_filterlist);
void set_format(gen_event_formatter::output_format of, const string& fmt) override;
~sinsp_evt_formatter(); ~sinsp_evt_formatter();
@ -57,6 +67,12 @@ public:
*/ */
bool resolve_tokens(sinsp_evt *evt, map<string,string>& values); bool resolve_tokens(sinsp_evt *evt, map<string,string>& values);
// For compatibility with gen_event_filter_factory
// interface. It just calls resolve_tokens().
bool get_field_values(gen_event *evt, std::map<std::string, std::string> &fields) override;
gen_event_formatter::output_format get_output_format() override;
/*! /*!
\brief Fills res with the string rendering of the event. \brief Fills res with the string rendering of the event.
@ -68,6 +84,11 @@ public:
*/ */
bool tostring(sinsp_evt* evt, OUT string* res); bool tostring(sinsp_evt* evt, OUT string* res);
// For compatibility with gen_event_formatter
bool tostring(gen_event* evt, std::string &output) override;
bool tostring_withformat(gen_event* evt, std::string &output, gen_event_formatter::output_format of) override;
/*! /*!
\brief Fills res with end of capture string rendering of the event. \brief Fills res with end of capture string rendering of the event.
\param res Pointer to the string that will be filled with the result. \param res Pointer to the string that will be filled with the result.
@ -78,13 +99,14 @@ public:
bool on_capture_end(OUT string* res); bool on_capture_end(OUT string* res);
private: private:
void set_format(const string& fmt); gen_event_formatter::output_format m_output_format;
// vector of (full string of the token, filtercheck) pairs // vector of (full string of the token, filtercheck) pairs
// e.g. ("proc.aname[2], ptr to sinsp_filter_check_thread) // e.g. ("proc.aname[2], ptr to sinsp_filter_check_thread)
vector<pair<string, sinsp_filter_check*>> m_tokens; vector<pair<string, sinsp_filter_check*>> m_tokens;
vector<uint32_t> m_tokenlens; vector<uint32_t> m_tokenlens;
sinsp* m_inspector; sinsp* m_inspector;
filter_check_list &m_available_checks;
bool m_require_all_values; bool m_require_all_values;
vector<sinsp_filter_check*> m_chks_to_free; vector<sinsp_filter_check*> m_chks_to_free;
@ -123,3 +145,23 @@ private:
sinsp *m_inspector; sinsp *m_inspector;
}; };
/*@}*/ /*@}*/
class sinsp_evt_formatter_factory : public gen_event_formatter_factory
{
public:
sinsp_evt_formatter_factory(sinsp *inspector, filter_check_list &available_checks=g_filterlist);
virtual ~sinsp_evt_formatter_factory();
void set_output_format(gen_event_formatter::output_format of) override;
std::shared_ptr<gen_event_formatter> create_formatter(const std::string &format) override;
protected:
// Maps from output string to formatter
std::map<std::string, std::shared_ptr<gen_event_formatter>> m_formatters;
sinsp *m_inspector;
filter_check_list &m_available_checks;
gen_event_formatter::output_format m_output_format;
};

View File

@ -133,6 +133,7 @@ std::string get_event_type(uint16_t type)
case PPME_SYSCALL_OPENAT_2_E: case PPME_SYSCALL_OPENAT_2_E:
case PPME_SYSCALL_OPENAT_X: case PPME_SYSCALL_OPENAT_X:
case PPME_SYSCALL_OPENAT_2_X: return "openat"; case PPME_SYSCALL_OPENAT_2_X: return "openat";
case PPME_SYSCALL_OPENAT2_X: return "openat2";
case PPME_SYSCALL_PIPE_E: case PPME_SYSCALL_PIPE_E:
case PPME_SYSCALL_PIPE_X: return "pipe"; case PPME_SYSCALL_PIPE_X: return "pipe";
case PPME_SYSCALL_POLL_E: case PPME_SYSCALL_POLL_E:
@ -233,6 +234,8 @@ std::string get_event_type(uint16_t type)
case PPME_SYSCALL_EXECVE_17_X: case PPME_SYSCALL_EXECVE_17_X:
case PPME_SYSCALL_EXECVE_18_X: case PPME_SYSCALL_EXECVE_18_X:
case PPME_SYSCALL_EXECVE_19_X: return "execve"; case PPME_SYSCALL_EXECVE_19_X: return "execve";
case PPME_SYSCALL_EXECVEAT_E:
case PPME_SYSCALL_EXECVEAT_X: return "execveat";
case PPME_SYSCALL_FCHDIR_E: case PPME_SYSCALL_FCHDIR_E:
case PPME_SYSCALL_FCHDIR_X: return "fchdir"; case PPME_SYSCALL_FCHDIR_X: return "fchdir";
case PPME_SYSCALL_FORK_E: case PPME_SYSCALL_FORK_E:

View File

@ -18,7 +18,6 @@ limitations under the License.
// //
// Various helper functions to render stuff on the screen // Various helper functions to render stuff on the screen
// //
#define __STDC_FORMAT_MACROS
#include <stdio.h> #include <stdio.h>
#include <iostream> #include <iostream>
#include <assert.h> #include <assert.h>
@ -27,120 +26,51 @@ limitations under the License.
#include <sinsp.h> #include <sinsp.h>
#include "fields_info.h" #include "fields_info.h"
// Must match the value in the zsh tab completion static void list_fields_markdown(std::list<gen_event_filter_factory::filter_fieldclass_info> &fld_classes)
#define DESCRIPTION_TEXT_START 16
#define CONSOLE_LINE_LEN 79
#define PRINTF_WRAP_CPROC(x) #x
#define PRINTF_WRAP(x) PRINTF_WRAP_CPROC(x)
void list_fields(bool verbose, bool markdown, bool names_only)
{ {
uint32_t j, l, m; for(auto &fld_class : fld_classes)
int32_t k;
if(markdown && !names_only)
{ {
printf("# Filter Fields List\n\n"); printf("\n## Field Class: %s\n\n", fld_class.name.c_str());
} printf("%s\n\n", fld_class.desc.c_str());
printf("Name | Type | Description\n");
printf(":----|:-----|:-----------\n");
vector<const filter_check_info*> fc_plugins; for(auto &fld_info : fld_class.fields)
sinsp::get_filtercheck_fields_info(&fc_plugins);
for(j = 0; j < fc_plugins.size(); j++)
{ {
const filter_check_info* fci = fc_plugins[j]; // Skip fields with the EPF_TABLE_ONLY flag.
if(fld_info.is_skippable())
if(fci->m_flags & filter_check_info::FL_HIDDEN)
{ {
continue; continue;
} }
if(!names_only) printf("`%s` | %s | %s\n", fld_info.name.c_str(), fld_info.data_type.c_str(), fld_info.desc.c_str());
}
}
}
void list_fields(bool verbose, bool markdown)
{ {
vector<const filter_check_info*> fc_plugins;
std::list<gen_event_filter_factory::filter_fieldclass_info> fld_classes;
sinsp::get_filtercheck_fields_info(fc_plugins);
fld_classes = sinsp_filter_factory::check_infos_to_fieldclass_infos(fc_plugins);
if(markdown) if(markdown)
{ {
printf("## Filter Class: %s\n\n", fci->m_name.c_str()); list_fields_markdown(fld_classes);
} }
else else
{ {
printf("\n----------------------\n"); for(auto &fld_class : fld_classes)
printf("Field Class: %s\n\n", fci->m_name.c_str());
}
}
for(k = 0; k < fci->m_nfields; k++)
{ {
const filtercheck_field_info* fld = &fci->m_fields[k]; printf("%s\n", fld_class.as_string(verbose).c_str());
if(fld->m_flags & EPF_TABLE_ONLY)
{
continue;
}
if(names_only)
{
printf("%s\n", fld->m_name);
}
else if(markdown)
{
printf("**Name**: %s \n", fld->m_name);
printf("**Description**: %s \n", fld->m_description);
printf("**Type**: %s \n\n", param_type_to_string(fld->m_type));
}
else
{
printf("%s", fld->m_name);
uint32_t namelen = (uint32_t)strlen(fld->m_name);
if(namelen >= DESCRIPTION_TEXT_START)
{
printf("\n");
namelen = 0;
}
for(l = 0; l < DESCRIPTION_TEXT_START - namelen; l++)
{
printf(" ");
}
string desc(fld->m_description);
if(fld->m_flags & EPF_FILTER_ONLY)
{
desc = "(FILTER ONLY) " + desc;
}
if(verbose)
{
desc += string(" Type:") + param_type_to_string(fld->m_type) + ".";
}
size_t desclen = desc.size();
for(l = 0; l < desclen; l++)
{
if(l % (CONSOLE_LINE_LEN - DESCRIPTION_TEXT_START) == 0 && l != 0)
{
printf("\n");
for(m = 0; m < DESCRIPTION_TEXT_START; m++)
{
printf(" ");
}
}
printf("%c", desc[l]);
}
printf("\n");
}
} }
} }
} }
void list_events(sinsp* inspector) void list_events(sinsp* inspector, bool markdown)
{ {
uint32_t j, k; uint32_t j, k;
string tstr; string tstr;
@ -148,6 +78,12 @@ void list_events(sinsp* inspector)
sinsp_evttables* einfo = inspector->get_event_info_tables(); sinsp_evttables* einfo = inspector->get_event_info_tables();
const struct ppm_event_info* etable = einfo->m_event_info; const struct ppm_event_info* etable = einfo->m_event_info;
if(markdown)
{
printf("Falco | Dir | Event\n");
printf(":-----|:----|:-----\n");
}
for(j = 0; j < PPM_EVENT_MAX; j++) for(j = 0; j < PPM_EVENT_MAX; j++)
{ {
const struct ppm_event_info ei = etable[j]; const struct ppm_event_info ei = etable[j];
@ -158,6 +94,31 @@ void list_events(sinsp* inspector)
continue; continue;
} }
if(markdown)
{
if(sinsp::simple_consumer_consider_evtnum(j))
{
printf("Yes");
} else {
printf("No");
}
printf(" | %c | **%s**(", dir, ei.name);
for(k = 0; k < ei.nparams; k++)
{
if(k != 0)
{
printf(", ");
}
printf("%s %s", param_type_to_string(ei.params[k].type),
ei.params[k].name);
}
printf(")\n");
} else
{
printf("%c %s(", dir, ei.name); printf("%c %s(", dir, ei.name);
for(k = 0; k < ei.nparams; k++) for(k = 0; k < ei.nparams; k++)
@ -174,3 +135,4 @@ void list_events(sinsp* inspector)
printf(")\n"); printf(")\n");
} }
} }
}

View File

@ -24,5 +24,5 @@ class sinsp;
// //
// Printer functions // Printer functions
// //
void list_fields(bool verbose, bool markdown, bool names_only=false); void list_fields(bool verbose, bool markdown=false);
void list_events(sinsp* inspector); void list_events(sinsp* inspector, bool markdown=false);

View File

@ -59,110 +59,6 @@ void *memmem(const void *haystack, size_t haystacklen, const void *needle, size_
#endif #endif
extern sinsp_filter_check_list g_filterlist;
///////////////////////////////////////////////////////////////////////////////
// sinsp_filter_check_list implementation
///////////////////////////////////////////////////////////////////////////////
sinsp_filter_check_list::sinsp_filter_check_list()
{
//////////////////////////////////////////////////////////////////////////////
// ADD NEW FILTER CHECK CLASSES HERE
//////////////////////////////////////////////////////////////////////////////
add_filter_check(new sinsp_filter_check_fd());
add_filter_check(new sinsp_filter_check_thread());
add_filter_check(new sinsp_filter_check_event());
add_filter_check(new sinsp_filter_check_user());
add_filter_check(new sinsp_filter_check_group());
add_filter_check(new sinsp_filter_check_syslog());
add_filter_check(new sinsp_filter_check_container());
add_filter_check(new sinsp_filter_check_utils());
add_filter_check(new sinsp_filter_check_fdlist());
#if !defined(CYGWING_AGENT) && !defined(MINIMAL_BUILD)
add_filter_check(new sinsp_filter_check_k8s());
add_filter_check(new sinsp_filter_check_mesos());
#endif // !defined(CYGWING_AGENT) && !defined(MINIMAL_BUILD)
add_filter_check(new sinsp_filter_check_tracer());
add_filter_check(new sinsp_filter_check_evtin());
}
sinsp_filter_check_list::~sinsp_filter_check_list()
{
uint32_t j;
for(j = 0; j < m_check_list.size(); j++)
{
delete m_check_list[j];
}
}
void sinsp_filter_check_list::add_filter_check(sinsp_filter_check* filter_check)
{
m_check_list.push_back(filter_check);
}
void sinsp_filter_check_list::get_all_fields(OUT vector<const filter_check_info*>* list)
{
uint32_t j;
for(j = 0; j < m_check_list.size(); j++)
{
list->push_back((const filter_check_info*)&(m_check_list[j]->m_info));
}
}
sinsp_filter_check* sinsp_filter_check_list::new_filter_check_from_fldname(const string& name,
sinsp* inspector,
bool do_exact_check)
{
uint32_t j;
for(j = 0; j < m_check_list.size(); j++)
{
m_check_list[j]->m_inspector = inspector;
int32_t fldnamelen = m_check_list[j]->parse_field_name(name.c_str(), false, true);
if(fldnamelen != -1)
{
if(do_exact_check)
{
if((int32_t)name.size() != fldnamelen)
{
goto field_not_found;
}
}
sinsp_filter_check* newchk = m_check_list[j]->allocate_new();
newchk->set_inspector(inspector);
return newchk;
}
}
field_not_found:
//
// If you are implementing a new filter check and this point is reached,
// it's very likely that you've forgotten to add your filter to the list in
// the constructor
//
return NULL;
}
sinsp_filter_check* sinsp_filter_check_list::new_filter_check_from_another(sinsp_filter_check *chk)
{
sinsp_filter_check *newchk = chk->allocate_new();
newchk->m_inspector = chk->m_inspector;
newchk->m_field_id = chk->m_field_id;
newchk->m_field = &chk->m_info.m_fields[chk->m_field_id];
newchk->m_boolop = chk->m_boolop;
newchk->m_cmpop = chk->m_cmpop;
return newchk;
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// type-based comparison functions // type-based comparison functions
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -512,6 +408,8 @@ bool flt_compare(cmpop op, ppm_param_type type, void* operand1, void* operand2,
case PT_ABSTIME: case PT_ABSTIME:
return flt_compare_uint64(op, *(uint64_t*)operand1, *(uint64_t*)operand2); return flt_compare_uint64(op, *(uint64_t*)operand1, *(uint64_t*)operand2);
case PT_CHARBUF: case PT_CHARBUF:
case PT_FSPATH:
case PT_FSRELPATH:
return flt_compare_string(op, (char*)operand1, (char*)operand2); return flt_compare_string(op, (char*)operand1, (char*)operand2);
case PT_BYTEBUF: case PT_BYTEBUF:
return flt_compare_buffer(op, (char*)operand1, (char*)operand2, op1_len, op2_len); return flt_compare_buffer(op, (char*)operand1, (char*)operand2, op1_len, op2_len);
@ -520,9 +418,7 @@ bool flt_compare(cmpop op, ppm_param_type type, void* operand1, void* operand2,
case PT_SOCKADDR: case PT_SOCKADDR:
case PT_SOCKTUPLE: case PT_SOCKTUPLE:
case PT_FDLIST: case PT_FDLIST:
case PT_FSPATH:
case PT_SIGSET: case PT_SIGSET:
case PT_FSRELPATH:
default: default:
ASSERT(false); ASSERT(false);
return false; return false;
@ -651,6 +547,28 @@ sinsp_filter_check::sinsp_filter_check()
m_val_storages = vector<vector<uint8_t>> (1, vector<uint8_t>(256)); m_val_storages = vector<vector<uint8_t>> (1, vector<uint8_t>(256));
m_val_storages_min_size = (numeric_limits<uint32_t>::max)(); m_val_storages_min_size = (numeric_limits<uint32_t>::max)();
m_val_storages_max_size = (numeric_limits<uint32_t>::min)(); m_val_storages_max_size = (numeric_limits<uint32_t>::min)();
// Do this once
if(s_all_event_types.size() == 0)
{
sinsp_evttables* einfo = m_inspector->get_event_info_tables();
const struct ppm_event_info* etable = einfo->m_event_info;
//
// Fill in from 2 to PPM_EVENT_MAX-1. 0 and 1 are excluded as
// those are PPM_GENERIC_E/PPME_GENERIC_X.
for(uint16_t i = 2; i < PPM_EVENT_MAX; i++)
{
// Skip "old" event versions that have been replaced
// by newer event versions, or events that are unused.
if(etable[i].flags & (EF_OLD_VERSION | EF_UNUSED))
{
continue;
}
s_all_event_types.insert(i);
}
}
} }
void sinsp_filter_check::set_inspector(sinsp* inspector) void sinsp_filter_check::set_inspector(sinsp* inspector)
@ -1090,16 +1008,14 @@ char* sinsp_filter_check::rawval_to_string(uint8_t* rawval,
return m_getpropertystr_storage; return m_getpropertystr_storage;
case PT_IPV6ADDR: case PT_IPV6ADDR:
{ {
char address[100]; char address[INET6_ADDRSTRLEN];
if(NULL == inet_ntop(AF_INET6, rawval, address, 100)) if(NULL == inet_ntop(AF_INET6, rawval, address, INET6_ADDRSTRLEN))
{ {
strcpy(address, "<NA>"); strcpy(address, "<NA>");
} }
strncpy(m_getpropertystr_storage, strlcpy(m_getpropertystr_storage, address, sizeof(m_getpropertystr_storage));
address,
100);
return m_getpropertystr_storage; return m_getpropertystr_storage;
} }
@ -1365,6 +1281,18 @@ uint8_t* sinsp_filter_check::extract_cached(sinsp_evt *evt, OUT uint32_t* len, b
} }
} }
const std::set<uint16_t> &sinsp_filter_check::evttypes()
{
// By default a check should be considered for all event types
// so use the default.
return s_all_event_types;
}
const std::set<uint16_t> &sinsp_filter_check::possible_evttypes()
{
return s_all_event_types;
}
bool sinsp_filter_check::compare(gen_event *evt) bool sinsp_filter_check::compare(gen_event *evt)
{ {
if(m_eval_cache_entry != NULL) if(m_eval_cache_entry != NULL)
@ -1967,7 +1895,6 @@ sinsp_filter* sinsp_filter_compiler::compile()
sinsp_filter* sinsp_filter_compiler::compile_() sinsp_filter* sinsp_filter_compiler::compile_()
{ {
m_scansize = (uint32_t)m_fltstr.size(); m_scansize = (uint32_t)m_fltstr.size();
while(true) while(true)
{ {
char a = next(); char a = next();
@ -2099,8 +2026,6 @@ sinsp_filter* sinsp_filter_compiler::compile_()
break; break;
} }
} }
vector<string> components = sinsp_split(m_fltstr, ' ');
return m_filter; return m_filter;
} }
@ -2396,8 +2321,9 @@ void sinsp_evttype_filter::syscalls_for_ruleset(std::vector<bool> &syscalls, uin
return m_rulesets[ruleset]->syscalls_for_ruleset(syscalls); return m_rulesets[ruleset]->syscalls_for_ruleset(syscalls);
} }
sinsp_filter_factory::sinsp_filter_factory(sinsp *inspector) sinsp_filter_factory::sinsp_filter_factory(sinsp *inspector,
: m_inspector(inspector) filter_check_list &available_checks)
: m_inspector(inspector), m_available_checks(available_checks)
{ {
} }
@ -2413,10 +2339,70 @@ gen_event_filter *sinsp_filter_factory::new_filter()
gen_event_filter_check *sinsp_filter_factory::new_filtercheck(const char *fldname) gen_event_filter_check *sinsp_filter_factory::new_filtercheck(const char *fldname)
{ {
return g_filterlist.new_filter_check_from_fldname(fldname, return m_available_checks.new_filter_check_from_fldname(fldname,
m_inspector, m_inspector,
true); true);
} }
std::list<gen_event_filter_factory::filter_fieldclass_info> sinsp_filter_factory::get_fields()
{
vector<const filter_check_info*> fc_plugins;
m_available_checks.get_all_fields(fc_plugins);
return check_infos_to_fieldclass_infos(fc_plugins);
}
std::list<gen_event_filter_factory::filter_fieldclass_info> sinsp_filter_factory::check_infos_to_fieldclass_infos(
const vector<const filter_check_info*> &fc_plugins)
{
std::list<gen_event_filter_factory::filter_fieldclass_info> ret;
for(auto &fci : fc_plugins)
{
if(fci->m_flags & filter_check_info::FL_HIDDEN)
{
continue;
}
gen_event_filter_factory::filter_fieldclass_info cinfo;
cinfo.name = fci->m_name;
cinfo.desc = fci->m_desc;
cinfo.shortdesc = fci->m_shortdesc;
for(int32_t k = 0; k < fci->m_nfields; k++)
{
const filtercheck_field_info* fld = &fci->m_fields[k];
// If a field is only used for stuff like
// chisels to organize events, we don't want
// to print it and don't return it here.
if(fld->m_flags & EPF_PRINT_ONLY)
{
continue;
}
gen_event_filter_factory::filter_field_info info;
info.name = fld->m_name;
info.desc = fld->m_description;
info.data_type = param_type_to_string(fld->m_type);
if(fld->m_flags & EPF_FILTER_ONLY)
{
info.tags.insert("FILTER ONLY");
}
if(fld->m_flags & EPF_TABLE_ONLY)
{
info.tags.insert("EPF_TABLE_ONLY");
}
cinfo.fields.emplace_back(std::move(info));
}
ret.emplace_back(std::move(cinfo));
}
return ret;
}
#endif // HAS_FILTERING #endif // HAS_FILTERING

View File

@ -22,6 +22,7 @@ limitations under the License.
#ifdef HAS_FILTERING #ifdef HAS_FILTERING
#include "filter_check_list.h"
#include "gen_filter.h" #include "gen_filter.h"
/** @defgroup filter Filtering events /** @defgroup filter Filtering events
@ -206,15 +207,26 @@ private:
class sinsp_filter_factory : public gen_event_filter_factory class sinsp_filter_factory : public gen_event_filter_factory
{ {
public: public:
sinsp_filter_factory(sinsp *inspector); sinsp_filter_factory(sinsp *inspector, filter_check_list &available_checks=g_filterlist);
virtual ~sinsp_filter_factory(); virtual ~sinsp_filter_factory();
gen_event_filter *new_filter(); gen_event_filter *new_filter();
gen_event_filter_check *new_filtercheck(const char *fldname); gen_event_filter_check *new_filtercheck(const char *fldname);
std::list<gen_event_filter_factory::filter_fieldclass_info> get_fields() override;
// Convienence method to convert a vector of
// filter_check_infos into a list of
// filter_fieldclass_infos. This is useful for programs that
// use filterchecks but not factories.
static std::list<filter_fieldclass_info> check_infos_to_fieldclass_infos(
const vector<const filter_check_info*> &fc_plugins);
protected: protected:
sinsp *m_inspector; sinsp *m_inspector;
filter_check_list &m_available_checks;
}; };
#endif // HAS_FILTERING #endif // HAS_FILTERING

View File

@ -0,0 +1,148 @@
/*
Copyright (C) 2021 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <cstdint>
#include "sinsp.h"
#ifdef HAS_FILTERING
#include "filter_check_list.h"
#include "filterchecks.h"
using namespace std;
///////////////////////////////////////////////////////////////////////////////
// sinsp_filter_check_list implementation
///////////////////////////////////////////////////////////////////////////////
filter_check_list::filter_check_list()
{
}
filter_check_list::~filter_check_list()
{
for(auto *chk : m_check_list)
{
delete chk;
}
}
void filter_check_list::add_filter_check(sinsp_filter_check* filter_check)
{
// If a filtercheck already exists with this name and
// shortdesc, don't add it--this can occur when plugins are
// loaded and set up gen_event_filter_checks to handle plugin
// events.
for(auto *chk : m_check_list)
{
if(chk->m_info.m_name == filter_check->m_info.m_name &&
chk->m_info.m_shortdesc == filter_check->m_info.m_shortdesc)
{
delete filter_check;
return;
}
}
m_check_list.push_back(filter_check);
}
void filter_check_list::get_all_fields(vector<const filter_check_info*>& list)
{
for(auto *chk : m_check_list)
{
list.push_back((const filter_check_info*)&(chk->m_info));
}
}
sinsp_filter_check* filter_check_list::new_filter_check_from_fldname(const string& name,
sinsp* inspector,
bool do_exact_check)
{
for(auto *chk : m_check_list)
{
chk->m_inspector = inspector;
int32_t fldnamelen = chk->parse_field_name(name.c_str(), false, true);
if(fldnamelen != -1)
{
if(do_exact_check)
{
if((int32_t)name.size() != fldnamelen)
{
goto field_not_found;
}
}
sinsp_filter_check* newchk = chk->allocate_new();
newchk->set_inspector(inspector);
return newchk;
}
}
field_not_found:
//
// If you are implementing a new filter check and this point is reached,
// it's very likely that you've forgotten to add your filter to the list in
// the constructor
//
return NULL;
}
sinsp_filter_check* filter_check_list::new_filter_check_from_another(sinsp_filter_check *chk)
{
sinsp_filter_check *newchk = chk->allocate_new();
newchk->m_inspector = chk->m_inspector;
newchk->m_field_id = chk->m_field_id;
newchk->m_field = &chk->m_info.m_fields[chk->m_field_id];
newchk->m_boolop = chk->m_boolop;
newchk->m_cmpop = chk->m_cmpop;
return newchk;
}
sinsp_filter_check_list::sinsp_filter_check_list()
{
//////////////////////////////////////////////////////////////////////////////
// ADD NEW FILTER CHECK CLASSES HERE
//////////////////////////////////////////////////////////////////////////////
add_filter_check(new sinsp_filter_check_gen_event());
add_filter_check(new sinsp_filter_check_event());
add_filter_check(new sinsp_filter_check_thread());
add_filter_check(new sinsp_filter_check_user());
add_filter_check(new sinsp_filter_check_group());
add_filter_check(new sinsp_filter_check_container());
add_filter_check(new sinsp_filter_check_fd());
add_filter_check(new sinsp_filter_check_syslog());
add_filter_check(new sinsp_filter_check_utils());
add_filter_check(new sinsp_filter_check_fdlist());
#if !defined(CYGWING_AGENT) && !defined(MINIMAL_BUILD)
add_filter_check(new sinsp_filter_check_k8s());
add_filter_check(new sinsp_filter_check_mesos());
#endif // !defined(CYGWING_AGENT) && !defined(MINIMAL_BUILD)
add_filter_check(new sinsp_filter_check_tracer());
add_filter_check(new sinsp_filter_check_evtin());
}
sinsp_filter_check_list::~sinsp_filter_check_list()
{
}
#endif // HAS_FILTERING

View File

@ -0,0 +1,58 @@
/*
Copyright (C) 2021 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include <string>
#include <vector>
#ifdef HAS_FILTERING
class sinsp_filter_check;
class filter_check_info;
class sinsp;
//
// Global class that stores the list of filtercheck plugins and offers
// functions to work with it.
//
class filter_check_list
{
public:
filter_check_list();
~filter_check_list();
void add_filter_check(sinsp_filter_check* filter_check);
void get_all_fields(std::vector<const filter_check_info*>& list);
sinsp_filter_check* new_filter_check_from_another(sinsp_filter_check *chk);
sinsp_filter_check* new_filter_check_from_fldname(const std::string& name, sinsp* inspector, bool do_exact_check);
protected:
std::vector<sinsp_filter_check*> m_check_list;
};
//
// This bakes in the "default" set of filterchecks that work with syscalls
class sinsp_filter_check_list : public filter_check_list
{
public:
sinsp_filter_check_list();
virtual ~sinsp_filter_check_list();
};
// This is the "default" filter check list
extern sinsp_filter_check_list g_filterlist;
#endif // HAS_FILTERING

View File

@ -27,6 +27,7 @@ limitations under the License.
#ifdef HAS_FILTERING #ifdef HAS_FILTERING
#include "filter.h" #include "filter.h"
#include "filterchecks.h" #include "filterchecks.h"
#include "plugin.h"
#include "protodecoder.h" #include "protodecoder.h"
#include "tracers.h" #include "tracers.h"
#include "value_parser.h" #include "value_parser.h"
@ -143,6 +144,7 @@ sinsp_filter_check_fd::sinsp_filter_check_fd()
m_fdinfo = NULL; m_fdinfo = NULL;
m_info.m_name = "fd"; m_info.m_name = "fd";
m_info.m_desc = "Every syscall that has a file descriptor in its arguments has these fields set with information related to the file.";
m_info.m_fields = sinsp_filter_check_fd_fields; m_info.m_fields = sinsp_filter_check_fd_fields;
m_info.m_nfields = sizeof(sinsp_filter_check_fd_fields) / sizeof(sinsp_filter_check_fd_fields[0]); m_info.m_nfields = sizeof(sinsp_filter_check_fd_fields) / sizeof(sinsp_filter_check_fd_fields[0]);
m_info.m_flags = filter_check_info::FL_WORKS_ON_THREAD_TABLE; m_info.m_flags = filter_check_info::FL_WORKS_ON_THREAD_TABLE;
@ -204,6 +206,7 @@ bool sinsp_filter_check_fd::extract_fdname_from_creator(sinsp_evt *evt, OUT uint
} }
case PPME_SYSCALL_OPENAT_X: case PPME_SYSCALL_OPENAT_X:
case PPME_SYSCALL_OPENAT_2_X: case PPME_SYSCALL_OPENAT_2_X:
case PPME_SYSCALL_OPENAT2_X:
{ {
sinsp_evt enter_evt; sinsp_evt enter_evt;
sinsp_evt_param *parinfo; sinsp_evt_param *parinfo;
@ -232,7 +235,7 @@ bool sinsp_filter_check_fd::extract_fdname_from_creator(sinsp_evt *evt, OUT uint
ASSERT(parinfo->m_len == sizeof(int64_t)); ASSERT(parinfo->m_len == sizeof(int64_t));
int64_t dirfd = *(int64_t *)parinfo->m_val; int64_t dirfd = *(int64_t *)parinfo->m_val;
sinsp_parser::parse_openat_dir(evt, name, dirfd, &sdir); sinsp_parser::parse_dirfd(evt, name, dirfd, &sdir);
char fullpath[SCAP_MAX_PATH_SIZE]; char fullpath[SCAP_MAX_PATH_SIZE];
@ -328,7 +331,8 @@ uint8_t* sinsp_filter_check_fd::extract_from_null_fd(sinsp_evt *evt, OUT uint32_
case TYPE_FILENAME: case TYPE_FILENAME:
{ {
if(evt->get_type() != PPME_SYSCALL_OPEN_E && evt->get_type() != PPME_SYSCALL_OPENAT_E && if(evt->get_type() != PPME_SYSCALL_OPEN_E && evt->get_type() != PPME_SYSCALL_OPENAT_E &&
evt->get_type() != PPME_SYSCALL_OPENAT_2_E && evt->get_type() != PPME_SYSCALL_CREAT_E) evt->get_type() != PPME_SYSCALL_OPENAT_2_E && evt->get_type() != PPME_SYSCALL_OPENAT2_E &&
evt->get_type() != PPME_SYSCALL_CREAT_E)
{ {
return NULL; return NULL;
} }
@ -363,6 +367,7 @@ uint8_t* sinsp_filter_check_fd::extract_from_null_fd(sinsp_evt *evt, OUT uint32_
case PPME_SYSCALL_OPEN_E: case PPME_SYSCALL_OPEN_E:
case PPME_SYSCALL_OPENAT_E: case PPME_SYSCALL_OPENAT_E:
case PPME_SYSCALL_OPENAT_2_E: case PPME_SYSCALL_OPENAT_2_E:
case PPME_SYSCALL_OPENAT2_E:
case PPME_SYSCALL_CREAT_E: case PPME_SYSCALL_CREAT_E:
m_tcstr[0] = CHAR_FD_FILE; m_tcstr[0] = CHAR_FD_FILE;
m_tcstr[1] = 0; m_tcstr[1] = 0;
@ -1238,7 +1243,7 @@ uint8_t* sinsp_filter_check_fd::extract(sinsp_evt *evt, OUT uint32_t* len, bool
break; break;
case TYPE_UID: case TYPE_UID:
{ {
if(evt->get_type() == PPME_CONTAINER_JSON_E) if(evt->get_type() == PPME_CONTAINER_JSON_E || evt->get_type() == PPME_CONTAINER_JSON_2_E)
{ {
return NULL; return NULL;
} }
@ -1854,11 +1859,13 @@ const filtercheck_field_info sinsp_filter_check_thread_fields[] =
{PT_BOOL, EPF_NONE, PF_NA, "proc.is_container_healthcheck", "true if this process is running as a part of the container's health check."}, {PT_BOOL, EPF_NONE, PF_NA, "proc.is_container_healthcheck", "true if this process is running as a part of the container's health check."},
{PT_BOOL, EPF_NONE, PF_NA, "proc.is_container_liveness_probe", "true if this process is running as a part of the container's liveness probe."}, {PT_BOOL, EPF_NONE, PF_NA, "proc.is_container_liveness_probe", "true if this process is running as a part of the container's liveness probe."},
{PT_BOOL, EPF_NONE, PF_NA, "proc.is_container_readiness_probe", "true if this process is running as a part of the container's readiness probe."}, {PT_BOOL, EPF_NONE, PF_NA, "proc.is_container_readiness_probe", "true if this process is running as a part of the container's readiness probe."},
{PT_BOOL, EPF_NONE, PF_NA, "proc.is_exe_writable", "true if this process' executable file is writable by the same user that spawned the process."},
}; };
sinsp_filter_check_thread::sinsp_filter_check_thread() sinsp_filter_check_thread::sinsp_filter_check_thread()
{ {
m_info.m_name = "process"; m_info.m_name = "process";
m_info.m_desc = "Additional information about the process and thread executing the syscall event.";
m_info.m_fields = sinsp_filter_check_thread_fields; m_info.m_fields = sinsp_filter_check_thread_fields;
m_info.m_nfields = sizeof(sinsp_filter_check_thread_fields) / sizeof(sinsp_filter_check_thread_fields[0]); m_info.m_nfields = sizeof(sinsp_filter_check_thread_fields) / sizeof(sinsp_filter_check_thread_fields[0]);
m_info.m_flags = filter_check_info::FL_WORKS_ON_THREAD_TABLE; m_info.m_flags = filter_check_info::FL_WORKS_ON_THREAD_TABLE;
@ -2120,7 +2127,7 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b
// Relying on the convention that a session id is the process id of the session leader // Relying on the convention that a session id is the process id of the session leader
// //
sinsp_threadinfo* sinfo = sinsp_threadinfo* sinfo =
&*m_inspector->get_thread_ref(tinfo->m_sid, false, true); m_inspector->get_thread_ref(tinfo->m_sid, false, true).get();
if(sinfo != NULL) if(sinfo != NULL)
{ {
@ -2307,7 +2314,7 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b
case TYPE_PNAME: case TYPE_PNAME:
{ {
sinsp_threadinfo* ptinfo = sinsp_threadinfo* ptinfo =
&*m_inspector->get_thread_ref(tinfo->m_ptid, false, true); m_inspector->get_thread_ref(tinfo->m_ptid, false, true).get();
if(ptinfo != NULL) if(ptinfo != NULL)
{ {
@ -2322,7 +2329,7 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b
case TYPE_PCMDLINE: case TYPE_PCMDLINE:
{ {
sinsp_threadinfo* ptinfo = sinsp_threadinfo* ptinfo =
&*m_inspector->get_thread_ref(tinfo->m_ptid, false, true); m_inspector->get_thread_ref(tinfo->m_ptid, false, true).get();
if(ptinfo != NULL) if(ptinfo != NULL)
{ {
@ -2651,6 +2658,9 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b
case TYPE_IS_CONTAINER_READINESS_PROBE: case TYPE_IS_CONTAINER_READINESS_PROBE:
m_tbool = (tinfo->m_category == sinsp_threadinfo::CAT_READINESS_PROBE); m_tbool = (tinfo->m_category == sinsp_threadinfo::CAT_READINESS_PROBE);
RETURN_EXTRACT_VAR(m_tbool); RETURN_EXTRACT_VAR(m_tbool);
case TYPE_IS_EXE_WRITABLE:
m_tbool = tinfo->m_exe_writable;
RETURN_EXTRACT_VAR(m_tbool);
default: default:
ASSERT(false); ASSERT(false);
return NULL; return NULL;
@ -2786,19 +2796,151 @@ bool sinsp_filter_check_thread::compare(sinsp_evt *evt)
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// sinsp_filter_check_event implementation // sinsp_filter_check_event implementation
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
const filtercheck_field_info sinsp_filter_check_event_fields[] = const filtercheck_field_info sinsp_filter_check_gen_event_fields[] =
{ {
{PT_UINT64, EPF_NONE, PF_ID, "evt.num", "event number."}, {PT_UINT64, EPF_NONE, PF_ID, "evt.num", "event number."},
{PT_CHARBUF, EPF_NONE, PF_NA, "evt.time", "event timestamp as a time string that includes the nanosecond part."}, {PT_CHARBUF, EPF_NONE, PF_NA, "evt.time", "event timestamp as a time string that includes the nanosecond part."},
{PT_CHARBUF, EPF_NONE, PF_NA, "evt.time.s", "event timestamp as a time string with no nanoseconds."}, {PT_CHARBUF, EPF_NONE, PF_NA, "evt.time.s", "event timestamp as a time string with no nanoseconds."},
{PT_CHARBUF, EPF_NONE, PF_NA, "evt.time.iso8601", "event timestamp in ISO 8601 format, including nanoseconds and time zone offset (in UTC)."}, {PT_CHARBUF, EPF_NONE, PF_NA, "evt.time.iso8601", "event timestamp in ISO 8601 format, including nanoseconds and time zone offset (in UTC)."},
{PT_CHARBUF, EPF_NONE, PF_NA, "evt.datetime", "event timestamp as a time string that includes the date."}, {PT_CHARBUF, EPF_NONE, PF_NA, "evt.datetime", "event timestamp as a time string that includes the date."},
{PT_CHARBUF, EPF_NONE, PF_NA, "evt.datetime.s", "event timestamp as a datetime string with no nanoseconds."},
{PT_ABSTIME, EPF_NONE, PF_DEC, "evt.rawtime", "absolute event timestamp, i.e. nanoseconds from epoch."}, {PT_ABSTIME, EPF_NONE, PF_DEC, "evt.rawtime", "absolute event timestamp, i.e. nanoseconds from epoch."},
{PT_ABSTIME, EPF_NONE, PF_DEC, "evt.rawtime.s", "integer part of the event timestamp (e.g. seconds since epoch)."}, {PT_ABSTIME, EPF_NONE, PF_DEC, "evt.rawtime.s", "integer part of the event timestamp (e.g. seconds since epoch)."},
{PT_ABSTIME, EPF_NONE, PF_10_PADDED_DEC, "evt.rawtime.ns", "fractional part of the absolute event timestamp."}, {PT_ABSTIME, EPF_NONE, PF_10_PADDED_DEC, "evt.rawtime.ns", "fractional part of the absolute event timestamp."},
{PT_RELTIME, EPF_NONE, PF_10_PADDED_DEC, "evt.reltime", "number of nanoseconds from the beginning of the capture."}, {PT_RELTIME, EPF_NONE, PF_10_PADDED_DEC, "evt.reltime", "number of nanoseconds from the beginning of the capture."},
{PT_RELTIME, EPF_NONE, PF_DEC, "evt.reltime.s", "number of seconds from the beginning of the capture."}, {PT_RELTIME, EPF_NONE, PF_DEC, "evt.reltime.s", "number of seconds from the beginning of the capture."},
{PT_RELTIME, EPF_NONE, PF_10_PADDED_DEC, "evt.reltime.ns", "fractional part (in ns) of the time from the beginning of the capture."}, {PT_RELTIME, EPF_NONE, PF_10_PADDED_DEC, "evt.reltime.ns", "fractional part (in ns) of the time from the beginning of the capture."},
{PT_CHARBUF, EPF_NONE, PF_NA, "evt.pluginname", "if the event comes from a plugin, the name of the plugin that generated it. The plugin must be currently loaded."},
{PT_CHARBUF, EPF_NONE, PF_NA, "evt.plugininfo", "if the event comes from a plugin, a summary of the event as formatted by the plugin. The plugin must be currently loaded."},
};
sinsp_filter_check_gen_event::sinsp_filter_check_gen_event()
{
m_info.m_name = "evt";
m_info.m_shortdesc = "All event types";
m_info.m_desc = "These fields can be used for all event types";
m_info.m_fields = sinsp_filter_check_gen_event_fields;
m_info.m_nfields = sizeof(sinsp_filter_check_gen_event_fields) / sizeof(sinsp_filter_check_gen_event_fields[0]);
m_u64val = 0;
}
sinsp_filter_check_gen_event::~sinsp_filter_check_gen_event()
{
}
sinsp_filter_check* sinsp_filter_check_gen_event::allocate_new()
{
return (sinsp_filter_check*) new sinsp_filter_check_gen_event();
}
Json::Value sinsp_filter_check_gen_event::extract_as_js(sinsp_evt *evt, OUT uint32_t* len)
{
switch(m_field_id)
{
case TYPE_TIME:
case TYPE_TIME_S:
case TYPE_TIME_ISO8601:
case TYPE_DATETIME:
case TYPE_DATETIME_S:
return (Json::Value::Int64)evt->get_ts();
case TYPE_RAWTS:
case TYPE_RAWTS_S:
case TYPE_RAWTS_NS:
case TYPE_RELTS:
case TYPE_RELTS_S:
case TYPE_RELTS_NS:
return (Json::Value::Int64)*(uint64_t*)extract(evt, len);
default:
return Json::nullValue;
}
return Json::nullValue;
}
uint8_t* sinsp_filter_check_gen_event::extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings)
{
std::shared_ptr<sinsp_plugin> plugin;
sinsp_source_plugin *splugin;
sinsp_evt_param *parinfo;
*len = 0;
switch(m_field_id)
{
case TYPE_TIME:
if(false)
{
m_strstorage = to_string(evt->get_ts());
}
else
{
sinsp_utils::ts_to_string(evt->get_ts(), &m_strstorage, false, true);
}
RETURN_EXTRACT_STRING(m_strstorage);
case TYPE_TIME_S:
sinsp_utils::ts_to_string(evt->get_ts(), &m_strstorage, false, false);
RETURN_EXTRACT_STRING(m_strstorage);
case TYPE_TIME_ISO8601:
sinsp_utils::ts_to_iso_8601(evt->get_ts(), &m_strstorage);
RETURN_EXTRACT_STRING(m_strstorage);
case TYPE_DATETIME:
sinsp_utils::ts_to_string(evt->get_ts(), &m_strstorage, true, true);
RETURN_EXTRACT_STRING(m_strstorage);
case TYPE_DATETIME_S:
sinsp_utils::ts_to_string(evt->get_ts(), &m_strstorage, true, false);
RETURN_EXTRACT_STRING(m_strstorage);
case TYPE_RAWTS:
m_u64val = evt->get_ts();
RETURN_EXTRACT_VAR(m_u64val);
case TYPE_RAWTS_S:
m_u64val = evt->get_ts() / ONE_SECOND_IN_NS;
RETURN_EXTRACT_VAR(m_u64val);
case TYPE_RAWTS_NS:
m_u64val = evt->get_ts() % ONE_SECOND_IN_NS;
RETURN_EXTRACT_VAR(m_u64val);
case TYPE_RELTS:
m_u64val = evt->get_ts() - m_inspector->m_firstevent_ts;
RETURN_EXTRACT_VAR(m_u64val);
case TYPE_RELTS_S:
m_u64val = (evt->get_ts() - m_inspector->m_firstevent_ts) / ONE_SECOND_IN_NS;
RETURN_EXTRACT_VAR(m_u64val);
case TYPE_RELTS_NS:
m_u64val = (evt->get_ts() - m_inspector->m_firstevent_ts) % ONE_SECOND_IN_NS;
RETURN_EXTRACT_VAR(m_u64val);
case TYPE_NUMBER:
m_u64val = evt->get_num();
RETURN_EXTRACT_VAR(m_u64val);
case TYPE_PLUGINNAME:
case TYPE_PLUGININFO:
plugin = m_inspector->get_plugin_by_evt(*evt);
if (plugin == nullptr)
{
return NULL;
}
if(m_field_id == TYPE_PLUGINNAME)
{
m_strstorage = plugin->name();
}
else
{
parinfo = evt->get_param(1);
splugin = static_cast<sinsp_source_plugin *>(plugin.get());
m_strstorage = splugin->event_to_string((const uint8_t *) parinfo->m_val, parinfo->m_len);
}
RETURN_EXTRACT_STRING(m_strstorage);
default:
ASSERT(false);
return NULL;
}
return NULL;
}
const filtercheck_field_info sinsp_filter_check_event_fields[] =
{
{PT_RELTIME, EPF_NONE, PF_DEC, "evt.latency", "delta between an exit event and the correspondent enter event, in nanoseconds."}, {PT_RELTIME, EPF_NONE, PF_DEC, "evt.latency", "delta between an exit event and the correspondent enter event, in nanoseconds."},
{PT_RELTIME, EPF_NONE, PF_DEC, "evt.latency.s", "integer part of the event latency delta."}, {PT_RELTIME, EPF_NONE, PF_DEC, "evt.latency.s", "integer part of the event latency delta."},
{PT_RELTIME, EPF_NONE, PF_10_PADDED_DEC, "evt.latency.ns", "fractional part of the event latency delta."}, {PT_RELTIME, EPF_NONE, PF_10_PADDED_DEC, "evt.latency.ns", "fractional part of the event latency delta."},
@ -2811,7 +2953,7 @@ const filtercheck_field_info sinsp_filter_check_event_fields[] =
{PT_CHARBUF, EPF_NONE, PF_DIR, "evt.dir", "event direction can be either '>' for enter events or '<' for exit events."}, {PT_CHARBUF, EPF_NONE, PF_DIR, "evt.dir", "event direction can be either '>' for enter events or '<' for exit events."},
{PT_CHARBUF, EPF_NONE, PF_NA, "evt.type", "The name of the event (e.g. 'open')."}, {PT_CHARBUF, EPF_NONE, PF_NA, "evt.type", "The name of the event (e.g. 'open')."},
{PT_UINT32, EPF_REQUIRES_ARGUMENT, PF_NA, "evt.type.is", "allows one to specify an event type, and returns 1 for events that are of that type. For example, evt.type.is.open returns 1 for open events, 0 for any other event."}, {PT_UINT32, EPF_REQUIRES_ARGUMENT, PF_NA, "evt.type.is", "allows one to specify an event type, and returns 1 for events that are of that type. For example, evt.type.is.open returns 1 for open events, 0 for any other event."},
{PT_CHARBUF, EPF_NONE, PF_NA, "syscall.type", "For system call events, the name of the system call (e.g. 'open'). Unset for other events (e.g. switch or sysdig internal events). Use this field instead of evt.type if you need to make sure that the filtered/printed value is actually a system call."}, {PT_CHARBUF, EPF_NONE, PF_NA, "syscall.type", "For system call events, the name of the system call (e.g. 'open'). Unset for other events (e.g. switch or internal events). Use this field instead of evt.type if you need to make sure that the filtered/printed value is actually a system call."},
{PT_CHARBUF, EPF_NONE, PF_NA, "evt.category", "The event category. Example values are 'file' (for file operations like open and close), 'net' (for network operations like socket and bind), memory (for things like brk or mmap), and so on."}, {PT_CHARBUF, EPF_NONE, PF_NA, "evt.category", "The event category. Example values are 'file' (for file operations like open and close), 'net' (for network operations like socket and bind), memory (for things like brk or mmap), and so on."},
{PT_INT16, EPF_NONE, PF_ID, "evt.cpu", "number of the CPU where this event happened."}, {PT_INT16, EPF_NONE, PF_ID, "evt.cpu", "number of the CPU where this event happened."},
{PT_CHARBUF, EPF_NONE, PF_NA, "evt.args", "all the event arguments, aggregated into a single string."}, {PT_CHARBUF, EPF_NONE, PF_NA, "evt.args", "all the event arguments, aggregated into a single string."},
@ -2849,19 +2991,21 @@ const filtercheck_field_info sinsp_filter_check_event_fields[] =
{PT_UINT64, EPF_TABLE_ONLY, PF_DEC, "evt.buflen.net", "the length of the binary data buffer, but only for network I/O events."}, {PT_UINT64, EPF_TABLE_ONLY, PF_DEC, "evt.buflen.net", "the length of the binary data buffer, but only for network I/O events."},
{PT_UINT64, EPF_TABLE_ONLY, PF_DEC, "evt.buflen.net.in", "the length of the binary data buffer, but only for input network I/O events."}, {PT_UINT64, EPF_TABLE_ONLY, PF_DEC, "evt.buflen.net.in", "the length of the binary data buffer, but only for input network I/O events."},
{PT_UINT64, EPF_TABLE_ONLY, PF_DEC, "evt.buflen.net.out", "the length of the binary data buffer, but only for output network I/O events."}, {PT_UINT64, EPF_TABLE_ONLY, PF_DEC, "evt.buflen.net.out", "the length of the binary data buffer, but only for output network I/O events."},
{PT_BOOL, EPF_NONE, PF_NA, "evt.is_open_read", "'true' for open/openat events where the path was opened for reading"}, {PT_BOOL, EPF_NONE, PF_NA, "evt.is_open_read", "'true' for open/openat/openat2 events where the path was opened for reading"},
{PT_BOOL, EPF_NONE, PF_NA, "evt.is_open_write", "'true' for open/openat events where the path was opened for writing"}, {PT_BOOL, EPF_NONE, PF_NA, "evt.is_open_write", "'true' for open/openat/openat2 events where the path was opened for writing"},
{PT_CHARBUF, EPF_TABLE_ONLY, PF_NA, "evt.infra.docker.name", "for docker infrastructure events, the name of the event."}, {PT_CHARBUF, EPF_TABLE_ONLY, PF_NA, "evt.infra.docker.name", "for docker infrastructure events, the name of the event."},
{PT_CHARBUF, EPF_TABLE_ONLY, PF_NA, "evt.infra.docker.container.id", "for docker infrastructure events, the id of the impacted container."}, {PT_CHARBUF, EPF_TABLE_ONLY, PF_NA, "evt.infra.docker.container.id", "for docker infrastructure events, the id of the impacted container."},
{PT_CHARBUF, EPF_TABLE_ONLY, PF_NA, "evt.infra.docker.container.name", "for docker infrastructure events, the name of the impacted container."}, {PT_CHARBUF, EPF_TABLE_ONLY, PF_NA, "evt.infra.docker.container.name", "for docker infrastructure events, the name of the impacted container."},
{PT_CHARBUF, EPF_TABLE_ONLY, PF_NA, "evt.infra.docker.container.image", "for docker infrastructure events, the image name of the impacted container."}, {PT_CHARBUF, EPF_TABLE_ONLY, PF_NA, "evt.infra.docker.container.image", "for docker infrastructure events, the image name of the impacted container."},
{PT_BOOL, EPF_NONE, PF_NA, "evt.is_open_exec", "'true' for open/openat or creat events where a file is created with execute permissions"}, {PT_BOOL, EPF_NONE, PF_NA, "evt.is_open_exec", "'true' for open/openat/openat2 or creat events where a file is created with execute permissions"},
}; };
sinsp_filter_check_event::sinsp_filter_check_event() sinsp_filter_check_event::sinsp_filter_check_event()
{ {
m_is_compare = false; m_is_compare = false;
m_info.m_name = "evt"; m_info.m_name = "evt";
m_info.m_shortdesc = "Syscall events only";
m_info.m_desc = "Event fields applicable to syscall events. Note that for most events you can access the individual arguments/parameters of each syscall via evt.arg, e.g. evt.arg.filename.";
m_info.m_fields = sinsp_filter_check_event_fields; m_info.m_fields = sinsp_filter_check_event_fields;
m_info.m_nfields = sizeof(sinsp_filter_check_event_fields) / sizeof(sinsp_filter_check_event_fields[0]); m_info.m_nfields = sizeof(sinsp_filter_check_event_fields) / sizeof(sinsp_filter_check_event_fields[0]);
m_u64val = 0; m_u64val = 0;
@ -3198,7 +3342,7 @@ uint8_t *sinsp_filter_check_event::extract_abspath(sinsp_evt *evt, OUT uint32_t
dirfdarg = "linkdirfd"; dirfdarg = "linkdirfd";
patharg = "linkpath"; patharg = "linkpath";
} }
else if(etype == PPME_SYSCALL_OPENAT_E || etype == PPME_SYSCALL_OPENAT_2_X) else if(etype == PPME_SYSCALL_OPENAT_E || etype == PPME_SYSCALL_OPENAT_2_X || etype == PPME_SYSCALL_OPENAT2_X)
{ {
dirfdarg = "dirfd"; dirfdarg = "dirfd";
patharg = "name"; patharg = "name";
@ -3339,19 +3483,9 @@ Json::Value sinsp_filter_check_event::extract_as_js(sinsp_evt *evt, OUT uint32_t
{ {
switch(m_field_id) switch(m_field_id)
{ {
case TYPE_TIME:
case TYPE_TIME_S:
case TYPE_TIME_ISO8601:
case TYPE_DATETIME:
case TYPE_RUNTIME_TIME_OUTPUT_FORMAT: case TYPE_RUNTIME_TIME_OUTPUT_FORMAT:
return (Json::Value::Int64)evt->get_ts(); return (Json::Value::Int64)evt->get_ts();
case TYPE_RAWTS:
case TYPE_RAWTS_S:
case TYPE_RAWTS_NS:
case TYPE_RELTS:
case TYPE_RELTS_S:
case TYPE_RELTS_NS:
case TYPE_LATENCY: case TYPE_LATENCY:
case TYPE_LATENCY_S: case TYPE_LATENCY_S:
case TYPE_LATENCY_NS: case TYPE_LATENCY_NS:
@ -3415,43 +3549,6 @@ uint8_t* sinsp_filter_check_event::extract(sinsp_evt *evt, OUT uint32_t* len, bo
*len = 0; *len = 0;
switch(m_field_id) switch(m_field_id)
{ {
case TYPE_TIME:
// if(g_filterchecks_force_raw_times)
if(false)
{
m_strstorage = to_string(evt->get_ts());
}
else
{
sinsp_utils::ts_to_string(evt->get_ts(), &m_strstorage, false, true);
}
RETURN_EXTRACT_STRING(m_strstorage);
case TYPE_TIME_S:
sinsp_utils::ts_to_string(evt->get_ts(), &m_strstorage, false, false);
RETURN_EXTRACT_STRING(m_strstorage);
case TYPE_TIME_ISO8601:
sinsp_utils::ts_to_iso_8601(evt->get_ts(), &m_strstorage);
RETURN_EXTRACT_STRING(m_strstorage);
case TYPE_DATETIME:
sinsp_utils::ts_to_string(evt->get_ts(), &m_strstorage, true, true);
RETURN_EXTRACT_STRING(m_strstorage);
case TYPE_RAWTS:
RETURN_EXTRACT_VAR(evt->m_pevt->ts);
case TYPE_RAWTS_S:
m_u64val = evt->get_ts() / ONE_SECOND_IN_NS;
RETURN_EXTRACT_VAR(m_u64val);
case TYPE_RAWTS_NS:
m_u64val = evt->get_ts() % ONE_SECOND_IN_NS;
RETURN_EXTRACT_VAR(m_u64val);
case TYPE_RELTS:
m_u64val = evt->get_ts() - m_inspector->m_firstevent_ts;
RETURN_EXTRACT_VAR(m_u64val);
case TYPE_RELTS_S:
m_u64val = (evt->get_ts() - m_inspector->m_firstevent_ts) / ONE_SECOND_IN_NS;
RETURN_EXTRACT_VAR(m_u64val);
case TYPE_RELTS_NS:
m_u64val = (evt->get_ts() - m_inspector->m_firstevent_ts) % ONE_SECOND_IN_NS;
RETURN_EXTRACT_VAR(m_u64val);
case TYPE_LATENCY: case TYPE_LATENCY:
{ {
m_u64val = 0; m_u64val = 0;
@ -3797,8 +3894,6 @@ uint8_t* sinsp_filter_check_event::extract(sinsp_evt *evt, OUT uint32_t* len, bo
} }
RETURN_EXTRACT_STRING(m_strstorage); RETURN_EXTRACT_STRING(m_strstorage);
case TYPE_NUMBER:
RETURN_EXTRACT_VAR(evt->m_evtnum);
case TYPE_CPU: case TYPE_CPU:
RETURN_EXTRACT_VAR(evt->m_cpuid); RETURN_EXTRACT_VAR(evt->m_cpuid);
case TYPE_ARGRAW: case TYPE_ARGRAW:
@ -4177,7 +4272,8 @@ uint8_t* sinsp_filter_check_event::extract(sinsp_evt *evt, OUT uint32_t* len, bo
if(etype == PPME_SYSCALL_OPEN_X || if(etype == PPME_SYSCALL_OPEN_X ||
etype == PPME_SYSCALL_CREAT_X || etype == PPME_SYSCALL_CREAT_X ||
etype == PPME_SYSCALL_OPENAT_X || etype == PPME_SYSCALL_OPENAT_X ||
etype == PPME_SYSCALL_OPENAT_2_X) etype == PPME_SYSCALL_OPENAT_2_X ||
etype == PPME_SYSCALL_OPENAT2_X)
{ {
return extract_error_count(evt, len); return extract_error_count(evt, len);
} }
@ -4253,6 +4349,7 @@ uint8_t* sinsp_filter_check_event::extract(sinsp_evt *evt, OUT uint32_t* len, bo
etype == PPME_SYSCALL_CREAT_X || etype == PPME_SYSCALL_CREAT_X ||
etype == PPME_SYSCALL_OPENAT_X || etype == PPME_SYSCALL_OPENAT_X ||
etype == PPME_SYSCALL_OPENAT_2_X || etype == PPME_SYSCALL_OPENAT_2_X ||
etype == PPME_SYSCALL_OPENAT2_X ||
etype == PPME_SOCKET_ACCEPT_X || etype == PPME_SOCKET_ACCEPT_X ||
etype == PPME_SOCKET_ACCEPT_5_X || etype == PPME_SOCKET_ACCEPT_5_X ||
etype == PPME_SOCKET_ACCEPT4_X || etype == PPME_SOCKET_ACCEPT4_X ||
@ -4400,11 +4497,13 @@ uint8_t* sinsp_filter_check_event::extract(sinsp_evt *evt, OUT uint32_t* len, bo
if(etype == PPME_SYSCALL_OPEN_X || if(etype == PPME_SYSCALL_OPEN_X ||
etype == PPME_SYSCALL_OPENAT_E || etype == PPME_SYSCALL_OPENAT_E ||
etype == PPME_SYSCALL_OPENAT_2_X) etype == PPME_SYSCALL_OPENAT_2_X ||
etype == PPME_SYSCALL_OPENAT2_X)
{ {
bool is_new_version = etype == PPME_SYSCALL_OPENAT_2_X || etype == PPME_SYSCALL_OPENAT2_X;
// For both OPEN_X and OPENAT_E, // For both OPEN_X and OPENAT_E,
// flags is the 3rd argument. // flags is the 3rd argument.
parinfo = evt->get_param(etype == PPME_SYSCALL_OPENAT_2_X ? 3 : 2); parinfo = evt->get_param(is_new_version ? 3 : 2);
ASSERT(parinfo->m_len == sizeof(uint32_t)); ASSERT(parinfo->m_len == sizeof(uint32_t));
uint32_t flags = *(uint32_t *)parinfo->m_val; uint32_t flags = *(uint32_t *)parinfo->m_val;
@ -4425,7 +4524,7 @@ uint8_t* sinsp_filter_check_event::extract(sinsp_evt *evt, OUT uint32_t* len, bo
if(m_field_id == TYPE_ISOPEN_EXEC && (flags & (PPM_O_TMPFILE | PPM_O_CREAT))) if(m_field_id == TYPE_ISOPEN_EXEC && (flags & (PPM_O_TMPFILE | PPM_O_CREAT)))
{ {
parinfo = evt->get_param(etype == PPME_SYSCALL_OPENAT_2_X ? 4 : 3); parinfo = evt->get_param(is_new_version ? 4 : 3);
ASSERT(parinfo->m_len == sizeof(uint32_t)); ASSERT(parinfo->m_len == sizeof(uint32_t));
uint32_t mode_bits = *(uint32_t *)parinfo->m_val; uint32_t mode_bits = *(uint32_t *)parinfo->m_val;
m_u32val = (mode_bits & is_exec_mask)? 1 : 0; m_u32val = (mode_bits & is_exec_mask)? 1 : 0;
@ -4524,6 +4623,80 @@ uint8_t* sinsp_filter_check_event::extract(sinsp_evt *evt, OUT uint32_t* len, bo
return NULL; return NULL;
} }
const std::set<uint16_t> &sinsp_filter_check_event::evttypes()
{
bool should_match = true;
if(m_field_id == TYPE_TYPE)
{
// The only meaningful comparison operators for this
// filter check should be direct comparisons and not
// LT/GE/CONTAINS/etc.
if(!(m_cmpop == CO_EQ || m_cmpop == CO_NE || m_cmpop == CO_IN))
{
m_event_types = possible_evttypes();
return m_event_types;
}
// If the comparison operator is !=, we need to invert
// the set. "not" is handled in
// gen_event_filter_expression::evttypes().
if(m_cmpop == CO_NE)
{
should_match = false;
}
}
else
{
// Should run for all event types
m_event_types = possible_evttypes();
return m_event_types;
}
// If here, we know we can find a more specific set of event types.
m_event_types.clear();
sinsp_evttables* einfo = m_inspector->get_event_info_tables();
const struct ppm_event_info* etable = einfo->m_event_info;
// This skips PPME_EVENT_GENERIC_{E,X} as the logical
// operators/inverse don't work for these values.
for(uint32_t i = 2; i < PPM_EVENT_MAX; i++)
{
// Skip "old" event versions that have been replaced
// by newer event versions, or events that are unused.
if(etable[i].flags & (EF_OLD_VERSION | EF_UNUSED))
{
continue;
}
// The values are held as strings, so we need to
// convert them back to numbers.
bool found = false;
for (uint16_t j=0; j < m_val_storages.size(); j++)
{
std::string evttype_str((char *) filter_value_p(j));
if(etable[i].name == evttype_str)
{
found = true;
break;
}
}
// We add to m_event_types if:
// - was found and should_match == true, or
// - was not found and should_match == false
// so found == should_match
if(found == should_match)
{
m_event_types.insert(i);
}
}
return m_event_types;
}
bool sinsp_filter_check_event::compare(sinsp_evt *evt) bool sinsp_filter_check_event::compare(sinsp_evt *evt)
{ {
bool res; bool res;
@ -4591,6 +4764,7 @@ const filtercheck_field_info sinsp_filter_check_user_fields[] =
sinsp_filter_check_user::sinsp_filter_check_user() sinsp_filter_check_user::sinsp_filter_check_user()
{ {
m_info.m_name = "user"; m_info.m_name = "user";
m_info.m_desc = "Information about the user executing the specific event.";
m_info.m_fields = sinsp_filter_check_user_fields; m_info.m_fields = sinsp_filter_check_user_fields;
m_info.m_nfields = sizeof(sinsp_filter_check_user_fields) / sizeof(sinsp_filter_check_user_fields[0]); m_info.m_nfields = sizeof(sinsp_filter_check_user_fields) / sizeof(sinsp_filter_check_user_fields[0]);
m_info.m_flags = filter_check_info::FL_WORKS_ON_THREAD_TABLE; m_info.m_flags = filter_check_info::FL_WORKS_ON_THREAD_TABLE;
@ -4663,6 +4837,7 @@ const filtercheck_field_info sinsp_filter_check_group_fields[] =
sinsp_filter_check_group::sinsp_filter_check_group() sinsp_filter_check_group::sinsp_filter_check_group()
{ {
m_info.m_name = "group"; m_info.m_name = "group";
m_info.m_desc = "Information about the user group.";
m_info.m_fields = sinsp_filter_check_group_fields; m_info.m_fields = sinsp_filter_check_group_fields;
m_info.m_nfields = sizeof(sinsp_filter_check_group_fields) / sizeof(sinsp_filter_check_group_fields[0]); m_info.m_nfields = sizeof(sinsp_filter_check_group_fields) / sizeof(sinsp_filter_check_group_fields[0]);
m_info.m_flags = filter_check_info::FL_WORKS_ON_THREAD_TABLE; m_info.m_flags = filter_check_info::FL_WORKS_ON_THREAD_TABLE;
@ -4692,25 +4867,11 @@ uint8_t* sinsp_filter_check_group::extract(sinsp_evt *evt, OUT uint32_t* len, bo
unordered_map<uint32_t, scap_groupinfo*>::iterator it; unordered_map<uint32_t, scap_groupinfo*>::iterator it;
ASSERT(m_inspector != NULL); ASSERT(m_inspector != NULL);
unordered_map<uint32_t, scap_groupinfo*>* grouplist = auto ginfo = m_inspector->get_group(tinfo->m_gid);
(unordered_map<uint32_t, scap_groupinfo*>*)m_inspector->get_grouplist(); if (ginfo == NULL)
ASSERT(grouplist->size() != 0);
if(tinfo->m_gid == 0xffffffff)
{ {
return NULL; return NULL;
} }
it = grouplist->find(tinfo->m_gid);
if(it == grouplist->end())
{
ASSERT(false);
return NULL;
}
scap_groupinfo* ginfo = it->second;
ASSERT(ginfo != NULL);
RETURN_EXTRACT_CSTR(ginfo->name); RETURN_EXTRACT_CSTR(ginfo->name);
} }
default: default:
@ -4743,15 +4904,16 @@ const filtercheck_field_info sinsp_filter_check_tracer_fields[] =
{PT_UINT64, EPF_TABLE_ONLY, PF_DEC, "span.count", "1 for span exit events."}, {PT_UINT64, EPF_TABLE_ONLY, PF_DEC, "span.count", "1 for span exit events."},
{PT_UINT64, (filtercheck_field_flags) (EPF_TABLE_ONLY | EPF_REQUIRES_ARGUMENT), PF_DEC, "span.count.fortag", "1 if the span's number of tags matches the field argument, and zero for all the other ones."}, {PT_UINT64, (filtercheck_field_flags) (EPF_TABLE_ONLY | EPF_REQUIRES_ARGUMENT), PF_DEC, "span.count.fortag", "1 if the span's number of tags matches the field argument, and zero for all the other ones."},
{PT_UINT64, (filtercheck_field_flags) (EPF_TABLE_ONLY | EPF_REQUIRES_ARGUMENT), PF_DEC, "span.childcount.fortag", "1 if the span's number of tags is greater than the field argument, and zero for all the other ones."}, {PT_UINT64, (filtercheck_field_flags) (EPF_TABLE_ONLY | EPF_REQUIRES_ARGUMENT), PF_DEC, "span.childcount.fortag", "1 if the span's number of tags is greater than the field argument, and zero for all the other ones."},
{PT_CHARBUF, (filtercheck_field_flags) (EPF_TABLE_ONLY | EPF_REQUIRES_ARGUMENT), PF_NA, "span.idtag", "id used by the span list csysdig view."}, {PT_CHARBUF, (filtercheck_field_flags) (EPF_TABLE_ONLY | EPF_REQUIRES_ARGUMENT), PF_NA, "span.idtag", "id used by the span list view."},
{PT_CHARBUF, EPF_TABLE_ONLY, PF_NA, "span.rawtime", "id used by the span list csysdig view."}, {PT_CHARBUF, EPF_TABLE_ONLY, PF_NA, "span.rawtime", "id used by the span list view."},
{PT_CHARBUF, EPF_TABLE_ONLY, PF_NA, "span.rawparenttime", "id used by the span list csysdig view."}, {PT_CHARBUF, EPF_TABLE_ONLY, PF_NA, "span.rawparenttime", "id used by the span list view."},
}; };
sinsp_filter_check_tracer::sinsp_filter_check_tracer() sinsp_filter_check_tracer::sinsp_filter_check_tracer()
{ {
m_storage = NULL; m_storage = NULL;
m_info.m_name = "span"; m_info.m_name = "span";
m_info.m_desc = "Fields used if information about distributed tracing is available.";
m_info.m_fields = sinsp_filter_check_tracer_fields; m_info.m_fields = sinsp_filter_check_tracer_fields;
m_info.m_nfields = sizeof(sinsp_filter_check_tracer_fields) / sizeof(sinsp_filter_check_tracer_fields[0]); m_info.m_nfields = sizeof(sinsp_filter_check_tracer_fields) / sizeof(sinsp_filter_check_tracer_fields[0]);
m_converter = new sinsp_filter_check_reference(); m_converter = new sinsp_filter_check_reference();
@ -5354,6 +5516,7 @@ sinsp_filter_check_evtin::sinsp_filter_check_evtin()
{ {
m_is_compare = false; m_is_compare = false;
m_info.m_name = "evtin"; m_info.m_name = "evtin";
m_info.m_desc = "Fields used if information about distributed tracing is available.";
m_info.m_fields = sinsp_filter_check_evtin_fields; m_info.m_fields = sinsp_filter_check_evtin_fields;
m_info.m_nfields = sizeof(sinsp_filter_check_evtin_fields) / sizeof(sinsp_filter_check_evtin_fields[0]); m_info.m_nfields = sizeof(sinsp_filter_check_evtin_fields) / sizeof(sinsp_filter_check_evtin_fields[0]);
m_u64val = 0; m_u64val = 0;
@ -5530,7 +5693,7 @@ inline uint8_t* sinsp_filter_check_evtin::extract_tracer(sinsp_evt *evt, sinsp_p
// //
// If this is a *.p.* field, reject anything that doesn't come from the same process // If this is a *.p.* field, reject anything that doesn't come from the same process
// //
sinsp_threadinfo* tinfo = &*m_inspector->get_thread_ref(pae->m_tid); sinsp_threadinfo* tinfo = m_inspector->get_thread_ref(pae->m_tid).get();
if(tinfo) if(tinfo)
{ {
@ -5551,7 +5714,7 @@ inline uint8_t* sinsp_filter_check_evtin::extract_tracer(sinsp_evt *evt, sinsp_p
// //
// If this is a *.p.* field, reject anything that doesn't share the same parent // If this is a *.p.* field, reject anything that doesn't share the same parent
// //
sinsp_threadinfo* tinfo = &*m_inspector->get_thread_ref(pae->m_tid); sinsp_threadinfo* tinfo = m_inspector->get_thread_ref(pae->m_tid).get();
if(tinfo) if(tinfo)
{ {
@ -5923,6 +6086,7 @@ const filtercheck_field_info sinsp_filter_check_syslog_fields[] =
sinsp_filter_check_syslog::sinsp_filter_check_syslog() sinsp_filter_check_syslog::sinsp_filter_check_syslog()
{ {
m_info.m_name = "syslog"; m_info.m_name = "syslog";
m_info.m_desc = "Content of Syslog messages.";
m_info.m_fields = sinsp_filter_check_syslog_fields; m_info.m_fields = sinsp_filter_check_syslog_fields;
m_info.m_nfields = sizeof(sinsp_filter_check_syslog_fields) / sizeof(sinsp_filter_check_syslog_fields[0]); m_info.m_nfields = sizeof(sinsp_filter_check_syslog_fields) / sizeof(sinsp_filter_check_syslog_fields[0]);
m_decoder = NULL; m_decoder = NULL;
@ -5981,7 +6145,7 @@ const filtercheck_field_info sinsp_filter_check_container_fields[] =
{ {
{PT_CHARBUF, EPF_NONE, PF_NA, "container.id", "the container id."}, {PT_CHARBUF, EPF_NONE, PF_NA, "container.id", "the container id."},
{PT_CHARBUF, EPF_NONE, PF_NA, "container.name", "the container name."}, {PT_CHARBUF, EPF_NONE, PF_NA, "container.name", "the container name."},
{PT_CHARBUF, EPF_NONE, PF_NA, "container.image", "the container image name (e.g. sysdig/sysdig:latest for docker, )."}, {PT_CHARBUF, EPF_NONE, PF_NA, "container.image", "the container image name (e.g. falcosecurity/falco:latest for docker)."},
{PT_CHARBUF, EPF_NONE, PF_NA, "container.image.id", "the container image id (e.g. 6f7e2741b66b)."}, {PT_CHARBUF, EPF_NONE, PF_NA, "container.image.id", "the container image id (e.g. 6f7e2741b66b)."},
{PT_CHARBUF, EPF_NONE, PF_NA, "container.type", "the container type, eg: docker or rkt"}, {PT_CHARBUF, EPF_NONE, PF_NA, "container.type", "the container type, eg: docker or rkt"},
{PT_BOOL, EPF_NONE, PF_NA, "container.privileged", "true for containers running as privileged, false otherwise"}, {PT_BOOL, EPF_NONE, PF_NA, "container.privileged", "true for containers running as privileged, false otherwise"},
@ -5992,7 +6156,7 @@ const filtercheck_field_info sinsp_filter_check_container_fields[] =
{PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "container.mount.mode", "the mount mode, specified by number (e.g. container.mount.mode[0]) or mount source (container.mount.mode[/usr/local]). The pathname can be a glob."}, {PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "container.mount.mode", "the mount mode, specified by number (e.g. container.mount.mode[0]) or mount source (container.mount.mode[/usr/local]). The pathname can be a glob."},
{PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "container.mount.rdwr", "the mount rdwr value, specified by number (e.g. container.mount.rdwr[0]) or mount source (container.mount.rdwr[/usr/local]). The pathname can be a glob."}, {PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "container.mount.rdwr", "the mount rdwr value, specified by number (e.g. container.mount.rdwr[0]) or mount source (container.mount.rdwr[/usr/local]). The pathname can be a glob."},
{PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "container.mount.propagation", "the mount propagation value, specified by number (e.g. container.mount.propagation[0]) or mount source (container.mount.propagation[/usr/local]). The pathname can be a glob."}, {PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "container.mount.propagation", "the mount propagation value, specified by number (e.g. container.mount.propagation[0]) or mount source (container.mount.propagation[/usr/local]). The pathname can be a glob."},
{PT_CHARBUF, EPF_NONE, PF_NA, "container.image.repository", "the container image repository (e.g. sysdig/sysdig)."}, {PT_CHARBUF, EPF_NONE, PF_NA, "container.image.repository", "the container image repository (e.g. falcosecurity/falco)."},
{PT_CHARBUF, EPF_NONE, PF_NA, "container.image.tag", "the container image tag (e.g. stable, latest)."}, {PT_CHARBUF, EPF_NONE, PF_NA, "container.image.tag", "the container image tag (e.g. stable, latest)."},
{PT_CHARBUF, EPF_NONE, PF_NA, "container.image.digest", "the container image registry digest (e.g. sha256:d977378f890d445c15e51795296e4e5062f109ce6da83e0a355fc4ad8699d27)."}, {PT_CHARBUF, EPF_NONE, PF_NA, "container.image.digest", "the container image registry digest (e.g. sha256:d977378f890d445c15e51795296e4e5062f109ce6da83e0a355fc4ad8699d27)."},
{PT_CHARBUF, EPF_NONE, PF_NA, "container.healthcheck", "The container's health check. Will be the null value (\"N/A\") if no healthcheck configured, \"NONE\" if configured but explicitly not created, and the healthcheck command line otherwise"}, {PT_CHARBUF, EPF_NONE, PF_NA, "container.healthcheck", "The container's health check. Will be the null value (\"N/A\") if no healthcheck configured, \"NONE\" if configured but explicitly not created, and the healthcheck command line otherwise"},
@ -6003,6 +6167,7 @@ const filtercheck_field_info sinsp_filter_check_container_fields[] =
sinsp_filter_check_container::sinsp_filter_check_container() sinsp_filter_check_container::sinsp_filter_check_container()
{ {
m_info.m_name = "container"; m_info.m_name = "container";
m_info.m_desc = "Container information. If the event is not happening inside a container, both id and name will be set to 'host'.";
m_info.m_fields = sinsp_filter_check_container_fields; m_info.m_fields = sinsp_filter_check_container_fields;
m_info.m_nfields = sizeof(sinsp_filter_check_container_fields) / sizeof(sinsp_filter_check_container_fields[0]); m_info.m_nfields = sizeof(sinsp_filter_check_container_fields) / sizeof(sinsp_filter_check_container_fields[0]);
m_info.m_flags = filter_check_info::FL_WORKS_ON_THREAD_TABLE; m_info.m_flags = filter_check_info::FL_WORKS_ON_THREAD_TABLE;
@ -6486,6 +6651,7 @@ uint8_t* sinsp_filter_check_container::extract(sinsp_evt *evt, OUT uint32_t* len
sinsp_filter_check_reference::sinsp_filter_check_reference() sinsp_filter_check_reference::sinsp_filter_check_reference()
{ {
m_info.m_name = "<NA>"; m_info.m_name = "<NA>";
m_info.m_desc = "";
m_info.m_fields = &m_finfo; m_info.m_fields = &m_finfo;
m_info.m_nfields = 1; m_info.m_nfields = 1;
m_info.m_flags = 0; m_info.m_flags = 0;
@ -6586,7 +6752,21 @@ char* sinsp_filter_check_reference::format_bytes(double val, uint32_t str_len, b
char* sinsp_filter_check_reference::format_time(uint64_t val, uint32_t str_len) char* sinsp_filter_check_reference::format_time(uint64_t val, uint32_t str_len)
{ {
if(val >= ONE_SECOND_IN_NS) if(val >= 3600 * ONE_SECOND_IN_NS)
{
snprintf(m_getpropertystr_storage,
sizeof(m_getpropertystr_storage),
"%.2u:%.2u:%.2u", (unsigned int)(val / (3600 * ONE_SECOND_IN_NS)),
(unsigned int)((val / (60 * ONE_SECOND_IN_NS)) % 60 ),
(unsigned int)((val / ONE_SECOND_IN_NS) % 60));
}
else if(val >= 60 * ONE_SECOND_IN_NS)
{
snprintf(m_getpropertystr_storage,
sizeof(m_getpropertystr_storage),
"%u:%u", (unsigned int)(val / (60 * ONE_SECOND_IN_NS)), (unsigned int)((val / ONE_SECOND_IN_NS) % 60));
}
else if(val >= ONE_SECOND_IN_NS)
{ {
snprintf(m_getpropertystr_storage, snprintf(m_getpropertystr_storage,
sizeof(m_getpropertystr_storage), sizeof(m_getpropertystr_storage),
@ -6866,6 +7046,7 @@ const filtercheck_field_info sinsp_filter_check_utils_fields[] =
sinsp_filter_check_utils::sinsp_filter_check_utils() sinsp_filter_check_utils::sinsp_filter_check_utils()
{ {
m_info.m_name = "util"; m_info.m_name = "util";
m_info.m_desc = "";
m_info.m_fields = sinsp_filter_check_utils_fields; m_info.m_fields = sinsp_filter_check_utils_fields;
m_info.m_nfields = sizeof(sinsp_filter_check_utils_fields) / sizeof(sinsp_filter_check_utils_fields[0]); m_info.m_nfields = sizeof(sinsp_filter_check_utils_fields) / sizeof(sinsp_filter_check_utils_fields[0]);
m_info.m_flags = filter_check_info::FL_HIDDEN; m_info.m_flags = filter_check_info::FL_HIDDEN;
@ -6909,6 +7090,7 @@ const filtercheck_field_info sinsp_filter_check_fdlist_fields[] =
sinsp_filter_check_fdlist::sinsp_filter_check_fdlist() sinsp_filter_check_fdlist::sinsp_filter_check_fdlist()
{ {
m_info.m_name = "fdlist"; m_info.m_name = "fdlist";
m_info.m_desc = "Poll event related fields.";
m_info.m_fields = sinsp_filter_check_fdlist_fields; m_info.m_fields = sinsp_filter_check_fdlist_fields;
m_info.m_nfields = sizeof(sinsp_filter_check_fdlist_fields) / sizeof(sinsp_filter_check_fdlist_fields[0]); m_info.m_nfields = sizeof(sinsp_filter_check_fdlist_fields) / sizeof(sinsp_filter_check_fdlist_fields[0]);
m_info.m_flags = filter_check_info::FL_WORKS_ON_THREAD_TABLE; m_info.m_flags = filter_check_info::FL_WORKS_ON_THREAD_TABLE;
@ -7135,6 +7317,7 @@ const filtercheck_field_info sinsp_filter_check_k8s_fields[] =
sinsp_filter_check_k8s::sinsp_filter_check_k8s() sinsp_filter_check_k8s::sinsp_filter_check_k8s()
{ {
m_info.m_name = "k8s"; m_info.m_name = "k8s";
m_info.m_desc = "Kubernetes related context. Available when configured to fetch k8s meta-data from API Server.";
m_info.m_fields = sinsp_filter_check_k8s_fields; m_info.m_fields = sinsp_filter_check_k8s_fields;
m_info.m_nfields = sizeof(sinsp_filter_check_k8s_fields) / sizeof(sinsp_filter_check_k8s_fields[0]); m_info.m_nfields = sizeof(sinsp_filter_check_k8s_fields) / sizeof(sinsp_filter_check_k8s_fields[0]);
m_info.m_flags = filter_check_info::FL_WORKS_ON_THREAD_TABLE; m_info.m_flags = filter_check_info::FL_WORKS_ON_THREAD_TABLE;
@ -7788,6 +7971,7 @@ const filtercheck_field_info sinsp_filter_check_mesos_fields[] =
sinsp_filter_check_mesos::sinsp_filter_check_mesos() sinsp_filter_check_mesos::sinsp_filter_check_mesos()
{ {
m_info.m_name = "mesos"; m_info.m_name = "mesos";
m_info.m_desc = "Mesos related context.";
m_info.m_fields = sinsp_filter_check_mesos_fields; m_info.m_fields = sinsp_filter_check_mesos_fields;
m_info.m_nfields = sizeof(sinsp_filter_check_mesos_fields) / sizeof(sinsp_filter_check_mesos_fields[0]); m_info.m_nfields = sizeof(sinsp_filter_check_mesos_fields) / sizeof(sinsp_filter_check_mesos_fields[0]);
m_info.m_flags = filter_check_info::FL_WORKS_ON_THREAD_TABLE; m_info.m_flags = filter_check_info::FL_WORKS_ON_THREAD_TABLE;

View File

@ -136,6 +136,9 @@ public:
return Json::nullValue; return Json::nullValue;
} }
virtual const std::set<uint16_t> &evttypes() override;
const std::set<uint16_t> &possible_evttypes() override;
// //
// Compare the field with the constant value obtained from parse_filter_value() // Compare the field with the constant value obtained from parse_filter_value()
// //
@ -193,29 +196,13 @@ protected:
private: private:
void set_inspector(sinsp* inspector); void set_inspector(sinsp* inspector);
friend class sinsp_filter_check_list; std::set<uint16_t> s_all_event_types;
friend class filter_check_list;
friend class sinsp_filter_optimizer; friend class sinsp_filter_optimizer;
friend class chk_compare_helper; friend class chk_compare_helper;
}; };
//
// Global class that stores the list of filtercheck plugins and offers
// functions to work with it.
//
class sinsp_filter_check_list
{
public:
sinsp_filter_check_list();
~sinsp_filter_check_list();
void add_filter_check(sinsp_filter_check* filter_check);
void get_all_fields(vector<const filter_check_info*>* list);
sinsp_filter_check* new_filter_check_from_another(sinsp_filter_check *chk);
sinsp_filter_check* new_filter_check_from_fldname(const string& name, sinsp* inspector, bool do_exact_check);
private:
vector<sinsp_filter_check*> m_check_list;
};
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Filter check classes // Filter check classes
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -366,6 +353,7 @@ public:
TYPE_IS_CONTAINER_HEALTHCHECK = 46, TYPE_IS_CONTAINER_HEALTHCHECK = 46,
TYPE_IS_CONTAINER_LIVENESS_PROBE = 47, TYPE_IS_CONTAINER_LIVENESS_PROBE = 47,
TYPE_IS_CONTAINER_READINESS_PROBE = 48, TYPE_IS_CONTAINER_READINESS_PROBE = 48,
TYPE_IS_EXE_WRITABLE = 49,
}; };
sinsp_filter_check_thread(); sinsp_filter_check_thread();
@ -394,9 +382,9 @@ private:
}; };
// //
// event checks // filterchecks that will work on any generic event
// //
class sinsp_filter_check_event : public sinsp_filter_check class sinsp_filter_check_gen_event : public sinsp_filter_check
{ {
public: public:
enum check_type enum check_type
@ -406,69 +394,94 @@ public:
TYPE_TIME_S = 2, TYPE_TIME_S = 2,
TYPE_TIME_ISO8601 = 3, TYPE_TIME_ISO8601 = 3,
TYPE_DATETIME = 4, TYPE_DATETIME = 4,
TYPE_RAWTS = 5, TYPE_DATETIME_S = 5,
TYPE_RAWTS_S = 6, TYPE_RAWTS = 6,
TYPE_RAWTS_NS = 7, TYPE_RAWTS_S = 7,
TYPE_RELTS = 8, TYPE_RAWTS_NS = 8,
TYPE_RELTS_S = 9, TYPE_RELTS = 9,
TYPE_RELTS_NS = 10, TYPE_RELTS_S = 10,
TYPE_LATENCY = 11, TYPE_RELTS_NS = 11,
TYPE_LATENCY_S = 12, TYPE_PLUGINNAME = 12,
TYPE_LATENCY_NS = 13, TYPE_PLUGININFO = 13,
TYPE_LATENCY_QUANTIZED = 14, };
TYPE_LATENCY_HUMAN = 15,
TYPE_DELTA = 16, sinsp_filter_check_gen_event();
TYPE_DELTA_S = 17, ~sinsp_filter_check_gen_event();
TYPE_DELTA_NS = 18, sinsp_filter_check* allocate_new();
TYPE_RUNTIME_TIME_OUTPUT_FORMAT = 19, uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings = true);
TYPE_DIR = 20, Json::Value extract_as_js(sinsp_evt *evt, OUT uint32_t* len);
TYPE_TYPE = 21,
TYPE_TYPE_IS = 22, uint64_t m_u64val;
TYPE_SYSCALL_TYPE = 23, string m_strstorage;
TYPE_CATEGORY = 24, };
TYPE_CPU = 25,
TYPE_ARGS = 26, //
TYPE_ARGSTR = 27, // event checks
TYPE_ARGRAW = 28, //
TYPE_INFO = 29, class sinsp_filter_check_event : public sinsp_filter_check
TYPE_BUFFER = 30, {
TYPE_BUFLEN = 31, public:
TYPE_RESSTR = 32, enum check_type
TYPE_RESRAW = 33, {
TYPE_FAILED = 34, TYPE_LATENCY = 0,
TYPE_ISIO = 35, TYPE_LATENCY_S = 1,
TYPE_ISIO_READ = 36, TYPE_LATENCY_NS = 2,
TYPE_ISIO_WRITE = 37, TYPE_LATENCY_QUANTIZED = 3,
TYPE_IODIR = 38, TYPE_LATENCY_HUMAN = 4,
TYPE_ISWAIT = 39, TYPE_DELTA = 5,
TYPE_WAIT_LATENCY = 40, TYPE_DELTA_S = 6,
TYPE_ISSYSLOG = 41, TYPE_DELTA_NS = 7,
TYPE_COUNT = 42, TYPE_RUNTIME_TIME_OUTPUT_FORMAT = 8,
TYPE_COUNT_ERROR = 43, TYPE_DIR = 9,
TYPE_COUNT_ERROR_FILE = 44, TYPE_TYPE = 10,
TYPE_COUNT_ERROR_NET = 45, TYPE_TYPE_IS = 11,
TYPE_COUNT_ERROR_MEMORY = 46, TYPE_SYSCALL_TYPE = 12,
TYPE_COUNT_ERROR_OTHER = 47, TYPE_CATEGORY = 13,
TYPE_COUNT_EXIT = 48, TYPE_CPU = 14,
TYPE_COUNT_PROCINFO = 49, TYPE_ARGS = 15,
TYPE_COUNT_THREADINFO = 50, TYPE_ARGSTR = 16,
TYPE_AROUND = 51, TYPE_ARGRAW = 17,
TYPE_ABSPATH = 52, TYPE_INFO = 18,
TYPE_BUFLEN_IN = 53, TYPE_BUFFER = 19,
TYPE_BUFLEN_OUT = 54, TYPE_BUFLEN = 20,
TYPE_BUFLEN_FILE = 55, TYPE_RESSTR = 21,
TYPE_BUFLEN_FILE_IN = 56, TYPE_RESRAW = 22,
TYPE_BUFLEN_FILE_OUT = 57, TYPE_FAILED = 23,
TYPE_BUFLEN_NET = 58, TYPE_ISIO = 24,
TYPE_BUFLEN_NET_IN = 59, TYPE_ISIO_READ = 25,
TYPE_BUFLEN_NET_OUT = 60, TYPE_ISIO_WRITE = 26,
TYPE_ISOPEN_READ = 61, TYPE_IODIR = 27,
TYPE_ISOPEN_WRITE = 62, TYPE_ISWAIT = 28,
TYPE_INFRA_DOCKER_NAME = 63, TYPE_WAIT_LATENCY = 29,
TYPE_INFRA_DOCKER_CONTAINER_ID = 64, TYPE_ISSYSLOG = 30,
TYPE_INFRA_DOCKER_CONTAINER_NAME = 65, TYPE_COUNT = 31,
TYPE_INFRA_DOCKER_CONTAINER_IMAGE = 66, TYPE_COUNT_ERROR = 32,
TYPE_ISOPEN_EXEC = 67, TYPE_COUNT_ERROR_FILE = 33,
TYPE_COUNT_ERROR_NET = 34,
TYPE_COUNT_ERROR_MEMORY = 35,
TYPE_COUNT_ERROR_OTHER = 36,
TYPE_COUNT_EXIT = 37,
TYPE_COUNT_PROCINFO = 38,
TYPE_COUNT_THREADINFO = 39,
TYPE_AROUND = 40,
TYPE_ABSPATH = 41,
TYPE_BUFLEN_IN = 42,
TYPE_BUFLEN_OUT = 43,
TYPE_BUFLEN_FILE = 44,
TYPE_BUFLEN_FILE_IN = 45,
TYPE_BUFLEN_FILE_OUT = 46,
TYPE_BUFLEN_NET = 47,
TYPE_BUFLEN_NET_IN = 48,
TYPE_BUFLEN_NET_OUT = 49,
TYPE_ISOPEN_READ = 50,
TYPE_ISOPEN_WRITE = 51,
TYPE_INFRA_DOCKER_NAME = 52,
TYPE_INFRA_DOCKER_CONTAINER_ID = 53,
TYPE_INFRA_DOCKER_CONTAINER_NAME = 54,
TYPE_INFRA_DOCKER_CONTAINER_IMAGE = 55,
TYPE_ISOPEN_EXEC = 56,
TYPE_PLUGIN_NAME = 57,
TYPE_PLUGIN_INFO = 58,
}; };
sinsp_filter_check_event(); sinsp_filter_check_event();
@ -482,6 +495,8 @@ public:
Json::Value extract_as_js(sinsp_evt *evt, OUT uint32_t* len); Json::Value extract_as_js(sinsp_evt *evt, OUT uint32_t* len);
bool compare(sinsp_evt *evt); bool compare(sinsp_evt *evt);
const std::set<uint16_t> &evttypes() override;
uint64_t m_u64val; uint64_t m_u64val;
uint64_t m_tsdelta; uint64_t m_tsdelta;
uint32_t m_u32val; uint32_t m_u32val;
@ -510,6 +525,8 @@ private:
uint32_t m_storage_size; uint32_t m_storage_size;
const char* m_cargname; const char* m_cargname;
sinsp_filter_check_reference* m_converter; sinsp_filter_check_reference* m_converter;
std::set<uint16_t> m_event_types;
}; };
// //

View File

@ -15,11 +15,16 @@ along with Falco. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <cstddef> #include <cstddef>
#include <iomanip>
#include <algorithm>
#include <sstream>
#include "stdint.h" #include "stdint.h"
#include "gen_filter.h" #include "gen_filter.h"
#include "sinsp.h" #include "sinsp.h"
#include "sinsp_int.h" #include "sinsp_int.h"
std::set<uint16_t> gen_event_filter_check::s_default_evttypes{1};
gen_event::gen_event() gen_event::gen_event()
{ {
} }
@ -58,6 +63,16 @@ int32_t gen_event_filter_check::get_check_id()
return m_check_id; return m_check_id;
} }
const std::set<uint16_t> &gen_event_filter_check::evttypes()
{
return s_default_evttypes;
}
const std::set<uint16_t> &gen_event_filter_check::possible_evttypes()
{
return s_default_evttypes;
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// gen_event_filter_expression implementation // gen_event_filter_expression implementation
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -199,6 +214,101 @@ int32_t gen_event_filter_expression::get_expr_boolop()
return b0; return b0;
} }
std::set<uint16_t> gen_event_filter_expression::inverse(const std::set<uint16_t> &evttypes)
{
std::set<uint16_t> ret;
// The inverse of "all events" is still "all events". This
// ensures that when no specific set of event types are named
// in the filter that the filter still runs for all event
// types.
if(evttypes == m_expr_possible_evttypes)
{
ret = evttypes;
return ret;
}
std::set_difference(m_expr_possible_evttypes.begin(), m_expr_possible_evttypes.end(),
evttypes.begin(), evttypes.end(),
std::inserter(ret, ret.begin()));
return ret;
}
void gen_event_filter_expression::combine_evttypes(boolop op,
const std::set<uint16_t> &chk_evttypes)
{
switch(op)
{
case BO_NONE:
// Overwrite with contents of set
// Should only occur for the first check in a list
m_expr_event_types = chk_evttypes;
break;
case BO_NOT:
m_expr_event_types = inverse(chk_evttypes);
break;
case BO_ORNOT:
combine_evttypes(BO_OR, inverse(chk_evttypes));
break;
case BO_ANDNOT:
combine_evttypes(BO_AND, inverse(chk_evttypes));
break;
case BO_OR:
// Merge the event types from the
// other set into this one.
m_expr_event_types.insert(chk_evttypes.begin(), chk_evttypes.end());
break;
case BO_AND:
// Set to the intersection of event types between this
// set and the provided set.
std::set<uint16_t> intersect;
std::set_intersection(m_expr_event_types.begin(), m_expr_event_types.end(),
chk_evttypes.begin(), chk_evttypes.end(),
std::inserter(intersect, intersect.begin()));
m_expr_event_types = intersect;
break;
}
}
const std::set<uint16_t> &gen_event_filter_expression::evttypes()
{
m_expr_event_types.clear();
m_expr_possible_evttypes = possible_evttypes();
for(uint32_t i = 0; i < m_checks.size(); i++)
{
gen_event_filter_check *chk = m_checks[i];
ASSERT(chk != NULL);
const std::set<uint16_t> &chk_evttypes = m_checks[i]->evttypes();
combine_evttypes(chk->m_boolop, chk_evttypes);
}
return m_expr_event_types;
}
const std::set<uint16_t> &gen_event_filter_expression::possible_evttypes()
{
// Return the set of possible event types from the first filtercheck.
if(m_checks.size() == 0)
{
// Shouldn't happen--every filter expression should have a
// real filtercheck somewhere below it.
ASSERT(false);
m_expr_possible_evttypes = s_default_evttypes;
}
else
{
m_expr_possible_evttypes = m_checks[0]->possible_evttypes();
}
return m_expr_possible_evttypes;
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// sinsp_filter implementation // sinsp_filter implementation
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -248,3 +358,146 @@ void gen_event_filter::add_check(gen_event_filter_check* chk)
{ {
m_curexpr->add_check((gen_event_filter_check *) chk); m_curexpr->add_check((gen_event_filter_check *) chk);
} }
std::set<uint16_t> gen_event_filter::evttypes()
{
return m_filter->evttypes();
}
bool gen_event_filter_factory::filter_field_info::is_skippable()
{
// Skip fields with the EPF_TABLE_ONLY flag.
return (tags.find("EPF_TABLE_ONLY") != tags.end());
}
uint32_t gen_event_filter_factory::filter_fieldclass_info::s_rightblock_start = 30;
uint32_t gen_event_filter_factory::filter_fieldclass_info::s_width = 120;
void gen_event_filter_factory::filter_fieldclass_info::wrapstring(const std::string &in, std::ostringstream &os)
{
std::istringstream is(in);
std::string word;
uint32_t len = 0;
while (is >> word)
{
// + 1 is trailing space.
uint32_t wordlen = word.length() + 1;
if((len + wordlen) <= (s_width-s_rightblock_start))
{
len += wordlen;
}
else
{
os << std::endl;
os << std::left << std::setw(s_rightblock_start) << " ";
len = wordlen;
}
os << word << " ";
}
}
std::string gen_event_filter_factory::filter_fieldclass_info::as_string(bool verbose, const std::set<std::string>& event_sources)
{
std::ostringstream os;
os << "-------------------------------" << std::endl;
os << std::left << std::setw(s_rightblock_start) << "Field Class:" << name;
if(shortdesc != "")
{
os << " (" << shortdesc << ")";
}
os << std::endl;
if(desc != "")
{
os << std::left << std::setw(s_rightblock_start) << "Description:";
wrapstring(desc, os);
os << std::endl;
}
if(!event_sources.empty())
{
os << std::left << std::setw(s_rightblock_start) << "Event Sources:";
for(const auto &src : event_sources)
{
os << src << " ";
}
os << std::endl;
}
os << std::endl;
for(auto &fld_info : fields)
{
// Skip fields that should not be included
// (e.g. hidden fields)
if(fld_info.is_skippable())
{
continue;
}
if(fld_info.name.length() > s_rightblock_start)
{
os << fld_info.name << std::endl;
os << std::left << std::setw(s_rightblock_start) << " ";
}
else
{
os << std::left << std::setw(s_rightblock_start) << fld_info.name;
}
// Append any tags, and if verbose, add the type, to the description.
std::string desc = fld_info.desc;
if(!fld_info.tags.empty())
{
std::string tagsstr = "(";
for(const auto &tag : fld_info.tags)
{
if(tagsstr != "(")
{
tagsstr += ",";
}
tagsstr += tag;
}
tagsstr += ")";
desc = tagsstr + " " + desc;
}
if(verbose)
{
desc = "(Type: " + fld_info.data_type + ") " + desc;
}
wrapstring(desc, os);
os << std::endl;
}
return os.str();
}
gen_event_formatter::gen_event_formatter()
{
}
gen_event_formatter::~gen_event_formatter()
{
}
gen_event_formatter_factory::gen_event_formatter_factory()
{
}
gen_event_formatter_factory::~gen_event_formatter_factory()
{
}

View File

@ -16,6 +16,11 @@ along with Falco. If not, see <http://www.gnu.org/licenses/>.
#pragma once #pragma once
#include <set>
#include <list>
#include <map>
#include <memory>
#include <string>
#include <vector> #include <vector>
/* /*
@ -115,6 +120,16 @@ public:
void set_check_id(int32_t id); void set_check_id(int32_t id);
virtual int32_t get_check_id(); virtual int32_t get_check_id();
// Return all event types used by this filtercheck. It's used in
// programs like falco to speed up rule evaluation.
virtual const std::set<uint16_t> &evttypes();
// Return all possible event types. Used for "not" operators
// where a set of events must be inverted.
virtual const std::set<uint16_t> &possible_evttypes();
static std::set<uint16_t> s_default_evttypes;
private: private:
int32_t m_check_id = 0; int32_t m_check_id = 0;
@ -160,8 +175,30 @@ public:
// //
int32_t get_expr_boolop(); int32_t get_expr_boolop();
// Return all event types used by this expression. It's used in
// programs like falco to speed up rule evaluation.
const std::set<uint16_t> &evttypes() override;
// An expression does not directly have a set of possible
// event types, but it can determine them from the m_checks
// vector.
const std::set<uint16_t> &possible_evttypes() override;
gen_event_filter_expression* m_parent; gen_event_filter_expression* m_parent;
std::vector<gen_event_filter_check*> m_checks; std::vector<gen_event_filter_check*> m_checks;
private:
std::set<uint16_t> m_expr_event_types;
std::set<uint16_t> m_expr_possible_evttypes;
// Return the "inverse" of the provided set of event types, using the
// provided full possible set of event types as a hint.
std::set<uint16_t> inverse(const std::set<uint16_t> &evttypes);
// Given a boolean op and a set of event types from a
// filtercheck in the expression, update m_expr_event_types appropriately.
void combine_evttypes(boolop op, const std::set<uint16_t> &evttypes);
}; };
@ -184,6 +221,10 @@ public:
void pop_expression(); void pop_expression();
void add_check(gen_event_filter_check* chk); void add_check(gen_event_filter_check* chk);
// Return all event types used by this filter. It's used in
// programs like falco to speed up rule evaluation.
std::set<uint16_t> evttypes();
gen_event_filter_expression* m_filter; gen_event_filter_expression* m_filter;
protected: protected:
@ -197,6 +238,58 @@ class gen_event_filter_factory
{ {
public: public:
// A struct describing a single filtercheck field ("ka.user")
struct filter_field_info
{
// The name of the field
std::string name;
// A description of the field
std::string desc;
// The data type for the field
std::string data_type;
// A set of free-form tags for the field. Examples include:
// FILTER ONLY: for fields that can only be used in filters, not outputs.
// IDX_REQUIRED: for fields that can take an optional index
// EPF_TABLE_ONLY: for fields with the EPF_TABLE_ONLY (e.g. hidden) flag set
// etc
std::set<std::string> tags;
bool is_skippable();
};
// Describes a group of filtercheck fields ("ka")
class filter_fieldclass_info
{
public:
// The name of the group of fields
std::string name;
// A description for the fields
std::string desc;
// A short (< 10 words) description of the fields. Can be blank.
std::string shortdesc;
std::list<filter_field_info> fields;
// Print a terminal-friendly representation of this
// field class, including name, description, supported
// event sources, and the name and description of each field.
std::string as_string(bool verbose, const std::set<std::string>& event_sources = std::set<std::string>());
// How far to right-justify the name/description/etc block.
static uint32_t s_rightblock_start;
// How wide the overall output should be.
static uint32_t s_width;
private:
void wrapstring(const std::string &in, std::ostringstream &os);
};
gen_event_filter_factory() {}; gen_event_filter_factory() {};
virtual ~gen_event_filter_factory() {}; virtual ~gen_event_filter_factory() {};
@ -205,4 +298,49 @@ public:
// Create a new filtercheck // Create a new filtercheck
virtual gen_event_filter_check *new_filtercheck(const char *fldname) = 0; virtual gen_event_filter_check *new_filtercheck(const char *fldname) = 0;
// Return the set of fields supported by this factory
virtual std::list<filter_fieldclass_info> get_fields() = 0;
};
class gen_event_formatter
{
public:
enum output_format {
OF_NORMAL = 0,
OF_JSON = 1
};
gen_event_formatter();
virtual ~gen_event_formatter();
virtual void set_format(output_format of, const std::string &format) = 0;
// Format the output string with the configured format
virtual bool tostring(gen_event *evt, std::string &output) = 0;
// In some cases, it may be useful to format an output string
// with a custom format.
virtual bool tostring_withformat(gen_event *evt, std::string &output, output_format of) = 0;
// The map should map from field name, without the '%'
// (e.g. "proc.name"), to field value (e.g. "nginx")
virtual bool get_field_values(gen_event *evt, std::map<std::string, std::string> &fields) = 0;
virtual output_format get_output_format() = 0;
};
class gen_event_formatter_factory
{
public:
gen_event_formatter_factory();
virtual ~gen_event_formatter_factory();
// This should be called before any calls to
// create_formatter(), and changes the output format of new
// formatters.
virtual void set_output_format(gen_event_formatter::output_format of) = 0;
virtual std::shared_ptr<gen_event_formatter> create_formatter(const std::string &format) = 0;
}; };

View File

@ -917,7 +917,6 @@ bool k8s_event_t::update(const Json::Value& item, k8s_state_t& state)
user_event_logger::log(evt, severity); user_event_logger::log(evt, severity);
// TODO: sysdig capture?
#endif // _WIN32 #endif // _WIN32
return true; return true;

View File

@ -180,8 +180,7 @@ void sinsp_logger::log(std::string msg, const severity sev)
if(m_flags & sinsp_logger::OT_ENCODE_SEV) if(m_flags & sinsp_logger::OT_ENCODE_SEV)
{ {
char sev_buf[ENCODE_LEN + 1]; char sev_buf[ENCODE_LEN + 1];
strncpy(sev_buf, encode_severity(sev), sizeof(sev_buf)); strlcpy(sev_buf, encode_severity(sev), sizeof(sev_buf));
sev_buf[sizeof(sev_buf) - 1] = 0;
msg.insert(0, sev_buf); msg.insert(0, sev_buf);
} }

View File

@ -43,7 +43,9 @@ bool should_drop(sinsp_evt *evt);
#endif #endif
#include "sinsp_int.h" #include "sinsp_int.h"
#include "container_engine/docker.h" #if !defined(MINIMAL_BUILD)
#include "container_engine/docker/async_source.h"
#endif
extern sinsp_protodecoder_list g_decoderlist; extern sinsp_protodecoder_list g_decoderlist;
extern sinsp_evttables g_infotables; extern sinsp_evttables g_infotables;
@ -109,6 +111,11 @@ void sinsp_parser::init_metaevt(metaevents_state& evt_state, uint16_t evt_type,
evt_state.m_metaevt.m_fdinfo = NULL; evt_state.m_metaevt.m_fdinfo = NULL;
} }
void sinsp_parser::set_track_connection_status(bool enabled)
{
m_track_connection_status = enabled;
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// PROCESSING ENTRY POINT // PROCESSING ENTRY POINT
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -123,12 +130,12 @@ void sinsp_parser::process_event(sinsp_evt *evt)
reset(evt); reset(evt);
// //
// When debug mode is not enabled, filter out events about sysdig itself // When debug mode is not enabled, filter out events about itself
// //
#if defined(HAS_CAPTURE) #if defined(HAS_CAPTURE)
if(is_live && !m_inspector->is_debug_enabled()) if(is_live && !m_inspector->is_debug_enabled())
{ {
if(evt->get_tid() == m_inspector->m_sysdig_pid && if(evt->get_tid() == m_inspector->m_self_pid &&
etype != PPME_SCHEDSWITCH_1_E && etype != PPME_SCHEDSWITCH_1_E &&
etype != PPME_SCHEDSWITCH_6_E && etype != PPME_SCHEDSWITCH_6_E &&
etype != PPME_DROP_E && etype != PPME_DROP_E &&
@ -136,7 +143,7 @@ void sinsp_parser::process_event(sinsp_evt *evt)
etype != PPME_SYSDIGEVENT_E && etype != PPME_SYSDIGEVENT_E &&
etype != PPME_PROCINFO_E && etype != PPME_PROCINFO_E &&
etype != PPME_CPU_HOTPLUG_E && etype != PPME_CPU_HOTPLUG_E &&
m_inspector->m_sysdig_pid) m_inspector->m_self_pid)
{ {
evt->m_filtered_out = true; evt->m_filtered_out = true;
return; return;
@ -262,6 +269,7 @@ void sinsp_parser::process_event(sinsp_evt *evt)
case PPME_SYSCALL_SETGID_E: case PPME_SYSCALL_SETGID_E:
case PPME_SYSCALL_EXECVE_18_E: case PPME_SYSCALL_EXECVE_18_E:
case PPME_SYSCALL_EXECVE_19_E: case PPME_SYSCALL_EXECVE_19_E:
case PPME_SYSCALL_EXECVEAT_E:
case PPME_SYSCALL_SETPGID_E: case PPME_SYSCALL_SETPGID_E:
store_event(evt); store_event(evt);
break; break;
@ -302,6 +310,7 @@ void sinsp_parser::process_event(sinsp_evt *evt)
case PPME_SYSCALL_CREAT_X: case PPME_SYSCALL_CREAT_X:
case PPME_SYSCALL_OPENAT_X: case PPME_SYSCALL_OPENAT_X:
case PPME_SYSCALL_OPENAT_2_X: case PPME_SYSCALL_OPENAT_2_X:
case PPME_SYSCALL_OPENAT2_X:
parse_open_openat_creat_exit(evt); parse_open_openat_creat_exit(evt);
break; break;
case PPME_SYSCALL_SELECT_E: case PPME_SYSCALL_SELECT_E:
@ -330,6 +339,7 @@ void sinsp_parser::process_event(sinsp_evt *evt)
case PPME_SYSCALL_EXECVE_17_X: case PPME_SYSCALL_EXECVE_17_X:
case PPME_SYSCALL_EXECVE_18_X: case PPME_SYSCALL_EXECVE_18_X:
case PPME_SYSCALL_EXECVE_19_X: case PPME_SYSCALL_EXECVE_19_X:
case PPME_SYSCALL_EXECVEAT_X:
parse_execve_exit(evt); parse_execve_exit(evt);
break; break;
case PPME_PROCEXIT_E: case PPME_PROCEXIT_E:
@ -433,6 +443,7 @@ void sinsp_parser::process_event(sinsp_evt *evt)
parse_container_evt(evt); // deprecated, only here for backwards compatibility parse_container_evt(evt); // deprecated, only here for backwards compatibility
break; break;
case PPME_CONTAINER_JSON_E: case PPME_CONTAINER_JSON_E:
case PPME_CONTAINER_JSON_2_E:
parse_container_json_evt(evt); parse_container_json_evt(evt);
break; break;
case PPME_CPU_HOTPLUG_E: case PPME_CPU_HOTPLUG_E:
@ -542,7 +553,7 @@ bool sinsp_parser::reset(sinsp_evt *evt)
// cleared in init(). So only keep the threadinfo for "live" // cleared in init(). So only keep the threadinfo for "live"
// containers. // containers.
// //
if (m_inspector->is_live() && etype == PPME_CONTAINER_JSON_E && evt->m_tinfo_ref != nullptr) if (m_inspector->is_live() && (etype == PPME_CONTAINER_JSON_E || etype == PPME_CONTAINER_JSON_2_E) && evt->m_tinfo_ref != nullptr)
{ {
// this is a synthetic event generated by the container manager // this is a synthetic event generated by the container manager
// the threadinfo should already be set properly // the threadinfo should already be set properly
@ -566,7 +577,7 @@ bool sinsp_parser::reset(sinsp_evt *evt)
{ {
if(etype == PPME_PROCINFO_E) if(etype == PPME_PROCINFO_E)
{ {
evt->m_tinfo = &*m_inspector->get_thread_ref(evt->m_pevt->tid, false, false); evt->m_tinfo = m_inspector->get_thread_ref(evt->m_pevt->tid, false, false).get();
} }
else else
{ {
@ -604,14 +615,14 @@ bool sinsp_parser::reset(sinsp_evt *evt)
query_os = true; query_os = true;
} }
if(etype == PPME_CONTAINER_JSON_E) if(etype == PPME_CONTAINER_JSON_E || etype == PPME_CONTAINER_JSON_2_E)
{ {
evt->m_tinfo = nullptr; evt->m_tinfo = nullptr;
return true; return true;
} }
else else
{ {
evt->m_tinfo = &*m_inspector->get_thread_ref(evt->m_pevt->tid, query_os, false); evt->m_tinfo = m_inspector->get_thread_ref(evt->m_pevt->tid, query_os, false).get();
} }
if(etype == PPME_SCHEDSWITCH_6_E) if(etype == PPME_SCHEDSWITCH_6_E)
@ -1098,7 +1109,7 @@ void sinsp_parser::parse_clone_exit(sinsp_evt *evt)
// //
// Lookup the thread that called clone() so we can copy its information // Lookup the thread that called clone() so we can copy its information
// //
sinsp_threadinfo* ptinfo = &*m_inspector->get_thread_ref(tid, true, true); sinsp_threadinfo* ptinfo = m_inspector->get_thread_ref(tid, true, true).get();
if(NULL == ptinfo) if(NULL == ptinfo)
{ {
// //
@ -1117,7 +1128,7 @@ void sinsp_parser::parse_clone_exit(sinsp_evt *evt)
// //
// See if the child is already there // See if the child is already there
// //
sinsp_threadinfo* child = &*m_inspector->get_thread_ref(childtid, false, true); sinsp_threadinfo* child = m_inspector->get_thread_ref(childtid, false, true).get();
if(NULL != child) if(NULL != child)
{ {
// //
@ -1161,6 +1172,9 @@ void sinsp_parser::parse_clone_exit(sinsp_evt *evt)
// Copy the full executable path from the parent // Copy the full executable path from the parent
tinfo->m_exepath = ptinfo->m_exepath; tinfo->m_exepath = ptinfo->m_exepath;
// Copy the exe writable metadata from the parent
tinfo->m_exe_writable = ptinfo->m_exe_writable;
// Copy the command arguments from the parent // Copy the command arguments from the parent
tinfo->m_args = ptinfo->m_args; tinfo->m_args = ptinfo->m_args;
@ -1191,8 +1205,8 @@ void sinsp_parser::parse_clone_exit(sinsp_evt *evt)
m_inspector->remove_thread(tid, true); m_inspector->remove_thread(tid, true);
tid_collision = true; tid_collision = true;
ptinfo = &*m_inspector->get_thread_ref(tid, ptinfo = m_inspector->get_thread_ref(tid,
true, true); true, true).get();
if(ptinfo == NULL) if(ptinfo == NULL)
{ {
@ -1566,7 +1580,7 @@ void sinsp_parser::parse_execve_exit(sinsp_evt *evt)
} }
// //
// We get here when execve returns. The thread has already been added by a previous fork or clone, // We get here when execve or execveat return. The thread has already been added by a previous fork or clone,
// and we just update the entry with the new information. // and we just update the entry with the new information.
// //
if(!evt->m_tinfo) if(!evt->m_tinfo)
@ -1597,6 +1611,7 @@ void sinsp_parser::parse_execve_exit(sinsp_evt *evt)
case PPME_SYSCALL_EXECVE_17_X: case PPME_SYSCALL_EXECVE_17_X:
case PPME_SYSCALL_EXECVE_18_X: case PPME_SYSCALL_EXECVE_18_X:
case PPME_SYSCALL_EXECVE_19_X: case PPME_SYSCALL_EXECVE_19_X:
case PPME_SYSCALL_EXECVEAT_X:
// Get the comm // Get the comm
parinfo = evt->get_param(13); parinfo = evt->get_param(13);
evt->m_tinfo->m_comm = parinfo->m_val; evt->m_tinfo->m_comm = parinfo->m_val;
@ -1642,6 +1657,7 @@ void sinsp_parser::parse_execve_exit(sinsp_evt *evt)
case PPME_SYSCALL_EXECVE_17_X: case PPME_SYSCALL_EXECVE_17_X:
case PPME_SYSCALL_EXECVE_18_X: case PPME_SYSCALL_EXECVE_18_X:
case PPME_SYSCALL_EXECVE_19_X: case PPME_SYSCALL_EXECVE_19_X:
case PPME_SYSCALL_EXECVEAT_X:
// Get the pgflt_maj // Get the pgflt_maj
parinfo = evt->get_param(8); parinfo = evt->get_param(8);
ASSERT(parinfo->m_len == sizeof(uint64_t)); ASSERT(parinfo->m_len == sizeof(uint64_t));
@ -1690,6 +1706,7 @@ void sinsp_parser::parse_execve_exit(sinsp_evt *evt)
case PPME_SYSCALL_EXECVE_17_X: case PPME_SYSCALL_EXECVE_17_X:
case PPME_SYSCALL_EXECVE_18_X: case PPME_SYSCALL_EXECVE_18_X:
case PPME_SYSCALL_EXECVE_19_X: case PPME_SYSCALL_EXECVE_19_X:
case PPME_SYSCALL_EXECVEAT_X:
// Get the environment // Get the environment
parinfo = evt->get_param(15); parinfo = evt->get_param(15);
evt->m_tinfo->set_env(parinfo->m_val, parinfo->m_len); evt->m_tinfo->set_env(parinfo->m_val, parinfo->m_len);
@ -1727,6 +1744,7 @@ void sinsp_parser::parse_execve_exit(sinsp_evt *evt)
case PPME_SYSCALL_EXECVE_17_X: case PPME_SYSCALL_EXECVE_17_X:
case PPME_SYSCALL_EXECVE_18_X: case PPME_SYSCALL_EXECVE_18_X:
case PPME_SYSCALL_EXECVE_19_X: case PPME_SYSCALL_EXECVE_19_X:
case PPME_SYSCALL_EXECVEAT_X:
// Get the tty // Get the tty
parinfo = evt->get_param(16); parinfo = evt->get_param(16);
ASSERT(parinfo->m_len == sizeof(int32_t)); ASSERT(parinfo->m_len == sizeof(int32_t));
@ -1765,6 +1783,57 @@ void sinsp_parser::parse_execve_exit(sinsp_evt *evt)
} }
} }
break; break;
case PPME_SYSCALL_EXECVEAT_X:
// Get exepath
if (retrieve_enter_event(enter_evt, evt))
{
/*
* Get dirfd
*/
parinfo = enter_evt->get_param(0);
ASSERT(parinfo->m_len == sizeof(int64_t));
int64_t dirfd = *(int64_t *)parinfo->m_val;
/*
* Get pathname
*/
parinfo = enter_evt->get_param(1);
if (strncmp(parinfo->m_val, "<NA>", 4) == 0)
{
evt->m_tinfo->m_exepath = "<NA>";
break;
}
char *pathname = parinfo->m_val;
uint32_t namelen = parinfo->m_len;
/*
* Get flags
*/
parinfo = enter_evt->get_param(2);
ASSERT(parinfo->m_len == sizeof(int32_t));
uint32_t flags = *(int32_t *)parinfo->m_val;
string sdir;
parse_dirfd(enter_evt, pathname, dirfd, &sdir);
/*
* If pathname is an empty string and the AT_EMPTY_PATH flag is specified then the file descriptor dirfd specifies the file to be executed (i.e., dirfd refers to an executable file, rather than a directory).
*/
if(flags & PPM_EXVAT_AT_EMPTY_PATH && strlen(pathname)==0)
{
// In this way, the pathname becomes an absolute path and in the 'concatenate_paths' function the dirfd value is not considered.
strcpy(pathname, sdir.c_str());
}
char fullpath[SCAP_MAX_PATH_SIZE];
sinsp_utils::concatenate_paths(fullpath, SCAP_MAX_PATH_SIZE,
sdir.c_str(),
(uint32_t)sdir.length(),
pathname,
namelen,
m_inspector->m_is_windows);
evt->m_tinfo->m_exepath = fullpath;
}
break;
default: default:
ASSERT(false); ASSERT(false);
} }
@ -1780,6 +1849,7 @@ void sinsp_parser::parse_execve_exit(sinsp_evt *evt)
case PPME_SYSCALL_EXECVE_18_X: case PPME_SYSCALL_EXECVE_18_X:
break; break;
case PPME_SYSCALL_EXECVE_19_X: case PPME_SYSCALL_EXECVE_19_X:
case PPME_SYSCALL_EXECVEAT_X:
// Get the vpgid // Get the vpgid
parinfo = evt->get_param(17); parinfo = evt->get_param(17);
ASSERT(parinfo->m_len == sizeof(int64_t)); ASSERT(parinfo->m_len == sizeof(int64_t));
@ -1808,6 +1878,16 @@ void sinsp_parser::parse_execve_exit(sinsp_evt *evt)
evt->m_tinfo->m_loginuid = *(uint32_t *) parinfo->m_val; evt->m_tinfo->m_loginuid = *(uint32_t *) parinfo->m_val;
} }
// Get execve flags
if(evt->get_num_params() > 19)
{
parinfo = evt->get_param(19);
ASSERT(parinfo->m_len == sizeof(uint32_t));
uint32_t flags = *(uint32_t *) parinfo->m_val;
evt->m_tinfo->m_exe_writable = ((flags & PPM_EXE_WRITABLE) != 0);
}
// //
// execve starts with a clean fd list, so we get rid of the fd list that clone // execve starts with a clean fd list, so we get rid of the fd list that clone
// copied from the parent // copied from the parent
@ -1852,7 +1932,7 @@ void sinsp_parser::parse_execve_exit(sinsp_evt *evt)
return; return;
} }
void sinsp_parser::parse_openat_dir(sinsp_evt *evt, char* name, int64_t dirfd, OUT string* sdir) void sinsp_parser::parse_dirfd(sinsp_evt *evt, char* name, int64_t dirfd, OUT string* sdir)
{ {
bool is_absolute = (name[0] == '/'); bool is_absolute = (name[0] == '/');
string tdirstr; string tdirstr;
@ -2029,7 +2109,7 @@ void sinsp_parser::parse_open_openat_creat_exit(sinsp_evt *evt)
} }
if(etype != PPME_SYSCALL_OPENAT_2_X) if(etype != PPME_SYSCALL_OPENAT_2_X && etype != PPME_SYSCALL_OPENAT2_X)
{ {
// //
// Load the enter event so we can access its arguments // Load the enter event so we can access its arguments
@ -2100,9 +2180,9 @@ void sinsp_parser::parse_open_openat_creat_exit(sinsp_evt *evt)
ASSERT(parinfo->m_len == sizeof(int64_t)); ASSERT(parinfo->m_len == sizeof(int64_t));
int64_t dirfd = *(int64_t *)parinfo->m_val; int64_t dirfd = *(int64_t *)parinfo->m_val;
parse_openat_dir(evt, name, dirfd, &sdir); parse_dirfd(evt, name, dirfd, &sdir);
} }
else if(etype == PPME_SYSCALL_OPENAT_2_X) else if(etype == PPME_SYSCALL_OPENAT_2_X || etype == PPME_SYSCALL_OPENAT2_X)
{ {
parinfo = evt->get_param(2); parinfo = evt->get_param(2);
name = parinfo->m_val; name = parinfo->m_val;
@ -2116,14 +2196,14 @@ void sinsp_parser::parse_open_openat_creat_exit(sinsp_evt *evt)
ASSERT(parinfo->m_len == sizeof(int64_t)); ASSERT(parinfo->m_len == sizeof(int64_t));
int64_t dirfd = *(int64_t *)parinfo->m_val; int64_t dirfd = *(int64_t *)parinfo->m_val;
if(evt->get_num_params() > 5) if(etype == PPME_SYSCALL_OPENAT_2_X && evt->get_num_params() > 5)
{ {
parinfo = evt->get_param(5); parinfo = evt->get_param(5);
ASSERT(parinfo->m_len == sizeof(uint32_t)); ASSERT(parinfo->m_len == sizeof(uint32_t));
dev = *(uint32_t *)parinfo->m_val; dev = *(uint32_t *)parinfo->m_val;
} }
parse_openat_dir(evt, name, dirfd, &sdir); parse_dirfd(evt, name, dirfd, &sdir);
} }
else else
{ {
@ -4331,7 +4411,7 @@ void sinsp_parser::parse_prlimit_exit(sinsp_evt *evt)
tid = evt->get_tid(); tid = evt->get_tid();
} }
sinsp_threadinfo* ptinfo = &*m_inspector->get_thread_ref(tid, true, true); sinsp_threadinfo* ptinfo = m_inspector->get_thread_ref(tid, true, true).get();
if(ptinfo == NULL) if(ptinfo == NULL)
{ {
ASSERT(false); ASSERT(false);
@ -4735,7 +4815,7 @@ void sinsp_parser::parse_container_json_evt(sinsp_evt *evt)
} }
#if !defined(MINIMAL_BUILD) && !defined(_WIN32) #if !defined(MINIMAL_BUILD) && !defined(_WIN32)
libsinsp::container_engine::docker::parse_json_mounts(container["Mounts"], container_info->m_mounts); libsinsp::container_engine::docker_async_source::parse_json_mounts(container["Mounts"], container_info->m_mounts);
#endif #endif
sinsp_container_info::container_health_probe::parse_health_probes(container, container_info->m_health_probes); sinsp_container_info::container_health_probe::parse_health_probes(container, container_info->m_health_probes);

View File

@ -55,7 +55,7 @@ public:
// //
// Combine the openat arguments into a full file name // Combine the openat arguments into a full file name
// //
static void parse_openat_dir(sinsp_evt *evt, char* name, int64_t dirfd, OUT string* sdir); static void parse_dirfd(sinsp_evt *evt, char* name, int64_t dirfd, OUT string* sdir);
// //
// Protocol decoder infrastructure methods // Protocol decoder infrastructure methods
@ -79,6 +79,7 @@ public:
// //
static void init_scapevt(metaevents_state& evt_state, uint16_t evt_type, uint16_t buf_size); static void init_scapevt(metaevents_state& evt_state, uint16_t evt_type, uint16_t buf_size);
void set_track_connection_status(bool enabled);
private: private:
// //
// Initializers // Initializers

1139
userspace/libsinsp/plugin.cpp Executable file

File diff suppressed because it is too large Load Diff

239
userspace/libsinsp/plugin.h Executable file
View File

@ -0,0 +1,239 @@
/*
Copyright (C) 2021 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include <atomic>
#include <chrono>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include <plugin_info.h>
#include "filter_check_list.h"
class sinsp_filter_check_plugin;
// Base class for source/extractor plugins. Can not be created directly.
class sinsp_plugin
{
public:
class version {
public:
version();
version(const std::string &version_str);
virtual ~version();
std::string as_string() const;
bool check(version &requested) const;
bool m_valid;
uint32_t m_version_major;
uint32_t m_version_minor;
uint32_t m_version_patch;
};
// Contains important info about a plugin, suitable for
// printing or other checks like compatibility.
struct info {
ss_plugin_type type;
std::string name;
std::string description;
std::string contact;
version plugin_version;
version required_api_version;
// Only filled in for source plugins
uint32_t id;
};
// Similar to struct ss_plugin_extract_field, but with c++
// types to avoid having to track memory allocations.
struct ext_field {
uint32_t field_id;
std::string field;
std::string arg;
uint32_t ftype;
bool field_present;
std::string res_str;
uint64_t res_u64;
};
// Create and register a plugin from a shared library pointed
// to by filepath, and add it to the inspector.
// Also create filterchecks for fields supported by the plugin
// and add them to the provided filter check list.
// The created sinsp_plugin is returned.
static std::shared_ptr<sinsp_plugin> register_plugin(sinsp* inspector,
std::string filepath,
const char* config,
filter_check_list &available_checks = g_filterlist);
// Create a plugin from the dynamic library at the provided
// path. On error, the shared_ptr will == NULL and errstr is
// set with an error.
static std::shared_ptr<sinsp_plugin> create_plugin(std::string &filepath, const char* config, std::string &errstr);
// Return a string with names/descriptions/etc of all plugins used by this inspector
static std::list<sinsp_plugin::info> plugin_infos(sinsp *inspector);
sinsp_plugin();
virtual ~sinsp_plugin();
// Given a dynamic library handle, fill in common properties
// (name/desc/etc) and required functions
// (init/destroy/extract/etc).
// Returns true on success, false + sets errstr on error.
virtual bool resolve_dylib_symbols(void *handle, std::string &errstr);
bool init(const char* config);
void destroy();
virtual ss_plugin_type type() = 0;
std::string get_last_error();
const std::string &name();
const std::string &description();
const std::string &contact();
const version &plugin_version();
const version &required_api_version();
const filtercheck_field_info *fields();
uint32_t nfields();
bool extract_field(ss_plugin_event &evt, sinsp_plugin::ext_field &field);
std::string get_init_schema(ss_plugin_schema_type& schema_type);
void validate_init_config(const char* config);
protected:
// Helper function to resolve symbols
static void* getsym(void* handle, const char* name, std::string &errstr);
// Helper function to set a string from an allocated charbuf and free the charbuf.
std::string str_from_alloc_charbuf(const char* charbuf);
// init() will call this to save the resulting state struct
virtual void set_plugin_state(ss_plugin_t *state) = 0;
virtual ss_plugin_t *plugin_state() = 0;
private:
// Functions common to all derived plugin
// types. get_required_api_version/get_type are common but not
// included here as they are called in create_plugin()
typedef struct {
const char* (*get_required_api_version)();
const char* (*get_init_schema)(ss_plugin_schema_type* schema_type);
ss_plugin_t* (*init)(const char* config, ss_plugin_rc* rc);
void (*destroy)(ss_plugin_t* s);
const char* (*get_last_error)(ss_plugin_t* s);
const char* (*get_name)();
const char* (*get_description)();
const char* (*get_contact)();
const char* (*get_version)();
const char* (*get_fields)();
ss_plugin_rc (*extract_fields)(ss_plugin_t *s, const ss_plugin_event *evt, uint32_t num_fields, ss_plugin_extract_field *fields);
} common_plugin_info;
std::string m_name;
std::string m_description;
std::string m_contact;
version m_plugin_version;
version m_required_api_version;
// Allocated instead of vector to match how it will be held in filter_check_info
std::unique_ptr<filtercheck_field_info[]> m_fields;
int32_t m_nfields;
common_plugin_info m_plugin_info;
void validate_init_config_json_schema(std::string config, std::string &schema);
};
// Note that this doesn't have a next_batch() method, as event generation is
// handled at the libscap level.
class sinsp_source_plugin : public sinsp_plugin
{
public:
// Describes a valid parameter for the open() function.
struct open_param {
std::string value;
std::string desc;
};
sinsp_source_plugin();
virtual ~sinsp_source_plugin();
bool resolve_dylib_symbols(void *handle, std::string &errstr) override;
ss_plugin_type type() override { return TYPE_SOURCE_PLUGIN; };
uint32_t id();
const std::string &event_source();
// For libscap that only works with struct of functions.
source_plugin_info *plugin_info();
// Note that embedding ss_instance_t in the object means that
// a plugin can only have one open active at a time.
bool open(const char* params, ss_plugin_rc &rc);
void close();
std::string get_progress(uint32_t &progress_pct);
std::string event_to_string(const uint8_t *data, uint32_t datalen);
std::vector<open_param> list_open_params();
protected:
void set_plugin_state(ss_plugin_t *state) override;
virtual ss_plugin_t *plugin_state() override;
private:
uint32_t m_id;
std::string m_event_source;
source_plugin_info m_source_plugin_info;
};
class sinsp_extractor_plugin : public sinsp_plugin
{
public:
sinsp_extractor_plugin();
virtual ~sinsp_extractor_plugin();
bool resolve_dylib_symbols(void *handle, std::string &errstr) override;
ss_plugin_type type() override { return TYPE_EXTRACTOR_PLUGIN; };
const std::set<std::string> &extract_event_sources();
// Return true if the provided source is compatible with this
// extractor plugin, either because the extractor plugin does
// not name any extract sources, or if the provided source is
// in the set of extract sources.
bool source_compatible(const std::string &source);
protected:
void set_plugin_state(ss_plugin_t *state) override;
virtual ss_plugin_t *plugin_state() override;
private:
extractor_plugin_info m_extractor_plugin_info;
std::set<std::string> m_extract_event_sources;
};

View File

@ -35,6 +35,7 @@ limitations under the License.
#include "cyclewriter.h" #include "cyclewriter.h"
#include "protodecoder.h" #include "protodecoder.h"
#include "dns_manager.h" #include "dns_manager.h"
#include "plugin.h"
#ifndef CYGWING_AGENT #ifndef CYGWING_AGENT
#ifndef MINIMAL_BUILD #ifndef MINIMAL_BUILD
@ -64,9 +65,11 @@ void on_new_entry_from_proc(void* context, scap_t* handle, int64_t tid, scap_thr
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
sinsp::sinsp(bool static_container, const std::string static_id, const std::string static_name, const std::string static_image) : sinsp::sinsp(bool static_container, const std::string static_id, const std::string static_name, const std::string static_image) :
m_external_event_processor(), m_external_event_processor(),
m_simpleconsumer(false),
m_evt(this), m_evt(this),
m_lastevent_ts(0), m_lastevent_ts(0),
m_container_manager(this, static_container, static_id, static_name, static_image), m_container_manager(this, static_container, static_id, static_name, static_image),
m_ppm_sc_of_interest(),
m_suppressed_comms() m_suppressed_comms()
{ {
#if !defined(MINIMAL_BUILD) && !defined(CYGWING_AGENT) && defined(HAS_CAPTURE) #if !defined(MINIMAL_BUILD) && !defined(CYGWING_AGENT) && defined(HAS_CAPTURE)
@ -115,7 +118,6 @@ sinsp::sinsp(bool static_container, const std::string static_id, const std::stri
m_last_procrequest_tod = 0; m_last_procrequest_tod = 0;
m_get_procs_cpu_from_driver = false; m_get_procs_cpu_from_driver = false;
m_is_tracers_capture_enabled = false; m_is_tracers_capture_enabled = false;
m_file_start_offset = 0;
m_flush_memory_dump = false; m_flush_memory_dump = false;
m_next_stats_print_time_ns = 0; m_next_stats_print_time_ns = 0;
m_large_envs_enabled = false; m_large_envs_enabled = false;
@ -126,7 +128,7 @@ sinsp::sinsp(bool static_container, const std::string static_id, const std::stri
m_print_container_data = false; m_print_container_data = false;
#if defined(HAS_CAPTURE) #if defined(HAS_CAPTURE)
m_sysdig_pid = getpid(); m_self_pid = getpid();
#endif #endif
uint32_t evlen = sizeof(scap_evt) + 2 * sizeof(uint16_t) + 2 * sizeof(uint64_t); uint32_t evlen = sizeof(scap_evt) + 2 * sizeof(uint16_t) + 2 * sizeof(uint64_t);
@ -162,6 +164,8 @@ sinsp::sinsp(bool static_container, const std::string static_id, const std::stri
#endif // !defined(CYGWING_AGENT) && !defined(MINIMAL_BUILD) #endif // !defined(CYGWING_AGENT) && !defined(MINIMAL_BUILD)
m_filter_proc_table_when_saving = false; m_filter_proc_table_when_saving = false;
m_replay_scap_evt = NULL;
} }
sinsp::~sinsp() sinsp::~sinsp()
@ -209,6 +213,7 @@ sinsp::~sinsp()
sinsp_dns_manager::get().cleanup(); sinsp_dns_manager::get().cleanup();
#endif #endif
#endif #endif
m_plugins_list.clear();
} }
void sinsp::add_protodecoders() void sinsp::add_protodecoders()
@ -328,35 +333,9 @@ void sinsp::init()
// //
if(is_capture()) if(is_capture())
{ {
uint64_t off = scap_ftell(m_h);
scap_evt* pevent; scap_evt* pevent;
uint16_t pcpuid; uint16_t pcpuid;
uint32_t ncnt = 0; sinsp_evt* tevt;
//
// Count how many container events we have
//
while(true)
{
int32_t res = scap_next(m_h, &pevent, &pcpuid);
if(res == SCAP_SUCCESS)
{
if((pevent->type != PPME_CONTAINER_E) && (pevent->type != PPME_CONTAINER_JSON_E))
{
break;
}
else
{
ncnt++;
continue;
}
}
else
{
break;
}
}
if (m_external_event_processor) if (m_external_event_processor)
{ {
@ -364,14 +343,33 @@ void sinsp::init()
} }
// //
// Rewind, reset the event count, and consume the exact number of events // Consume every container event we have
// //
scap_fseek(m_h, off); while(true)
scap_event_reset_count(m_h); {
for(uint32_t j = 0; j < ncnt; j++) int32_t res = scap_next(m_h, &pevent, &pcpuid);
if(res == SCAP_SUCCESS)
{
// Setting these to non-null will make sinsp::next use them as a scap event
// to avoid a call to scap_next. In this way, we can avoid the state parsing phase
// once we reach a container-unrelated event.
m_replay_scap_evt = pevent;
m_replay_scap_cpuid = pcpuid;
if((pevent->type != PPME_CONTAINER_E) && (pevent->type != PPME_CONTAINER_JSON_E) && (pevent->type != PPME_CONTAINER_JSON_2_E))
{
break;
}
else
{ {
sinsp_evt* tevt;
next(&tevt); next(&tevt);
continue;
}
}
else
{
break;
}
} }
} }
@ -428,7 +426,7 @@ void sinsp::init()
#if defined(HAS_CAPTURE) #if defined(HAS_CAPTURE)
if(m_mode == SCAP_MODE_LIVE) if(m_mode == SCAP_MODE_LIVE)
{ {
if(scap_getpid_global(m_h, &m_sysdig_pid) != SCAP_SUCCESS) if(scap_getpid_global(m_h, &m_self_pid) != SCAP_SUCCESS)
{ {
ASSERT(false); ASSERT(false);
} }
@ -441,6 +439,39 @@ void sinsp::set_import_users(bool import_users)
m_import_users = import_users; m_import_users = import_users;
} }
void sinsp::fill_syscalls_of_interest(scap_open_args *oargs)
{
// Fallback to set all events as interesting
if (m_mode != SCAP_MODE_LIVE || m_ppm_sc_of_interest.empty())
{
for(int i = 0; i < PPM_SC_MAX; i++)
{
m_ppm_sc_of_interest.insert(i);
}
}
/*
* in case of simple consumer (driver side)
* set all droppable events as non interesting (if they are syscall driven)
*/
if (m_simpleconsumer)
{
for (int i = 0; i < PPM_SC_MAX; i++)
{
if (g_infotables.m_syscall_info_table[i].flags & EF_DROP_SIMPLE_CONS)
{
m_ppm_sc_of_interest.erase(i);
}
}
}
// Finally, set scap_open_args syscalls_of_interest
for (int i = 0; i < PPM_SC_MAX; i++)
{
oargs->ppm_sc_of_interest.ppm_sc[i] = m_ppm_sc_of_interest.find(i) != m_ppm_sc_of_interest.end();
}
}
void sinsp::open_live_common(uint32_t timeout_ms, scap_mode_t mode) void sinsp::open_live_common(uint32_t timeout_ms, scap_mode_t mode)
{ {
char error[SCAP_LASTERR_SIZE]; char error[SCAP_LASTERR_SIZE];
@ -457,12 +488,15 @@ void sinsp::open_live_common(uint32_t timeout_ms, scap_mode_t mode)
// //
m_mode = mode; m_mode = mode;
scap_open_args oargs; scap_open_args oargs;
oargs.mode = mode; oargs.mode = mode;
oargs.fname = NULL; oargs.fname = NULL;
oargs.proc_callback = NULL; oargs.proc_callback = NULL;
oargs.proc_callback_context = NULL; oargs.proc_callback_context = NULL;
oargs.udig = m_udig; oargs.udig = m_udig;
fill_syscalls_of_interest(&oargs);
if(!m_filter_proc_table_when_saving) if(!m_filter_proc_table_when_saving)
{ {
oargs.proc_callback = ::on_new_entry_from_proc; oargs.proc_callback = ::on_new_entry_from_proc;
@ -483,6 +517,19 @@ void sinsp::open_live_common(uint32_t timeout_ms, scap_mode_t mode)
add_suppressed_comms(oargs); add_suppressed_comms(oargs);
//
// If a plugin was configured, pass it to scap and set the capture mode to
// SCAP_MODE_PLUGIN.
//
if(m_input_plugin)
{
sinsp_source_plugin *splugin = static_cast<sinsp_source_plugin *>(m_input_plugin.get());
oargs.input_plugin = splugin->plugin_info();
oargs.input_plugin_params = (char*)m_input_plugin_open_params.c_str();
m_mode = SCAP_MODE_PLUGIN;
oargs.mode = SCAP_MODE_PLUGIN;
}
int32_t scap_rc; int32_t scap_rc;
m_h = scap_open(oargs, error, &scap_rc); m_h = scap_open(oargs, error, &scap_rc);
@ -533,6 +580,7 @@ void sinsp::open_nodriver()
oargs.proc_callback_context = this; oargs.proc_callback_context = this;
} }
oargs.import_users = m_import_users; oargs.import_users = m_import_users;
fill_syscalls_of_interest(&oargs);
int32_t scap_rc; int32_t scap_rc;
m_h = scap_open(oargs, error, &scap_rc); m_h = scap_open(oargs, error, &scap_rc);
@ -547,6 +595,11 @@ void sinsp::open_nodriver()
init(); init();
} }
void sinsp::set_simple_consumer()
{
m_simpleconsumer = true;
}
int64_t sinsp::get_file_size(const std::string& fname, char *error) int64_t sinsp::get_file_size(const std::string& fname, char *error)
{ {
static string err_str = "Could not determine capture file size: "; static string err_str = "Could not determine capture file size: ";
@ -573,7 +626,7 @@ int64_t sinsp::get_file_size(const std::string& fname, char *error)
} }
#endif #endif
if(errdesc.empty()) errdesc = get_error_desc(err_str); if(errdesc.empty()) errdesc = get_error_desc(err_str);
strncpy(error, errdesc.c_str(), errdesc.size() > SCAP_LASTERR_SIZE ? SCAP_LASTERR_SIZE : errdesc.size()); strlcpy(error, errdesc.c_str(), SCAP_LASTERR_SIZE);
return -1; return -1;
} }
@ -664,18 +717,13 @@ void sinsp::open_int()
oargs.proc_callback = NULL; oargs.proc_callback = NULL;
oargs.proc_callback_context = NULL; oargs.proc_callback_context = NULL;
oargs.import_users = m_import_users; oargs.import_users = m_import_users;
if(m_file_start_offset != 0)
{
oargs.start_offset = m_file_start_offset;
}
else
{
oargs.start_offset = 0; oargs.start_offset = 0;
} fill_syscalls_of_interest(&oargs);
add_suppressed_comms(oargs); add_suppressed_comms(oargs);
int32_t scap_rc; int32_t scap_rc;
m_h = scap_open(oargs, error, &scap_rc); m_h = scap_open(oargs, error, &scap_rc);
if(m_h == NULL) if(m_h == NULL)
@ -741,11 +789,7 @@ void sinsp::close()
m_is_dumping = false; m_is_dumping = false;
if(NULL != m_network_interfaces) deinit_state();
{
delete m_network_interfaces;
m_network_interfaces = NULL;
}
#ifdef HAS_FILTERING #ifdef HAS_FILTERING
if(m_filter != NULL) if(m_filter != NULL)
@ -760,6 +804,23 @@ void sinsp::close()
m_evttype_filter = NULL; m_evttype_filter = NULL;
} }
#endif #endif
m_ppm_sc_of_interest.clear();
}
//
// This deinitializes the sinsp internal state, and it's used
// internally while closing or restarting the capture.
//
void sinsp::deinit_state()
{
if(NULL != m_network_interfaces)
{
delete m_network_interfaces;
m_network_interfaces = NULL;
}
m_thread_manager->clear();
} }
void sinsp::autodump_start(const string& dump_filename, bool compress) void sinsp::autodump_start(const string& dump_filename, bool compress)
@ -1015,30 +1076,34 @@ void schedule_next_threadinfo_evt(sinsp* _this, void* data)
} }
} }
void sinsp::restart_capture_at_filepos(uint64_t filepos) //
// This restarts the current event capture. This de-initializes and
// re-initializes the internal state of both sinsp and scap, and is
// supported only for opened captures with mode SCAP_MODE_CAPTURE.
// This resets the internal states on-the-fly, which is ideally equivalent
// to closing and then re-opening the capture, but avoids losing the passed
// configurations and reuses the same underlying scap event source.
//
void sinsp::restart_capture()
{ {
// // Save state info that could be lost during de-initialization
// Backup a couple of settings uint64_t nevts = m_nevts;
//
uint64_t evtnum = m_nevts;
string filterstring = m_filterstring;
// // De-initialize the insternal state
// Close and reopen the capture deinit_state();
//
m_file_start_offset = filepos;
close();
open_int();
// // Restart the scap capture, which also trigger a re-initialization of
// Set again the backuped settings // scap's internal state.
// if (scap_restart_capture(m_h) != SCAP_SUCCESS)
m_evt.m_evtnum = evtnum;
m_nevts = evtnum;
if(filterstring != "")
{ {
set_filter(filterstring); throw sinsp_exception(string("scap error: ") + scap_getlasterr(m_h));
} }
// Re-initialize the internal state
init();
// Restore the saved state info
m_nevts = nevts;
} }
uint64_t sinsp::max_buf_used() uint64_t sinsp::max_buf_used()
@ -1142,7 +1207,20 @@ int32_t sinsp::next(OUT sinsp_evt **puevt)
// //
// Get the event from libscap // Get the event from libscap
// //
if (m_replay_scap_evt != NULL)
{
// Replay the last event, if we saved one
res = SCAP_SUCCESS;
evt->m_pevt = m_replay_scap_evt;
evt->m_cpuid = m_replay_scap_cpuid;
m_replay_scap_evt = NULL;
}
else
{
// If no last event was saved, invoke
// the actual scap_next
res = scap_next(m_h, &(evt->m_pevt), &(evt->m_cpuid)); res = scap_next(m_h, &(evt->m_pevt), &(evt->m_cpuid));
}
if(res != SCAP_SUCCESS) if(res != SCAP_SUCCESS)
{ {
@ -1164,8 +1242,11 @@ int32_t sinsp::next(OUT sinsp_evt **puevt)
} }
else if(res == SCAP_UNEXPECTED_BLOCK) else if(res == SCAP_UNEXPECTED_BLOCK)
{ {
uint64_t filepos = scap_ftell(m_h) - scap_get_unexpected_block_readsize(m_h); // This mostly happens in concatenated scap files, where an unexpected block
restart_capture_at_filepos(filepos); // represents the end of a file and the start of the next appended one.
// In this case, we restart the capture so that the internal states gets reset
// and the blocks coming from the next appended file get consumed.
restart_capture();
return SCAP_TIMEOUT; return SCAP_TIMEOUT;
} }
@ -1180,7 +1261,7 @@ int32_t sinsp::next(OUT sinsp_evt **puevt)
uint64_t ts = evt->get_ts(); uint64_t ts = evt->get_ts();
if(m_firstevent_ts == 0 && evt->m_pevt->type != PPME_CONTAINER_JSON_E) if(m_firstevent_ts == 0 && evt->m_pevt->type != PPME_CONTAINER_JSON_E && evt->m_pevt->type != PPME_CONTAINER_JSON_2_E)
{ {
m_firstevent_ts = ts; m_firstevent_ts = ts;
} }
@ -1272,7 +1353,7 @@ int32_t sinsp::next(OUT sinsp_evt **puevt)
if(nfdr != 0) if(nfdr != 0)
{ {
sinsp_threadinfo* ptinfo = &*get_thread_ref(m_tid_of_fd_to_remove, true, true); sinsp_threadinfo* ptinfo = get_thread_ref(m_tid_of_fd_to_remove, true, true).get();
if(!ptinfo) if(!ptinfo)
{ {
ASSERT(false); ASSERT(false);
@ -1585,6 +1666,100 @@ void sinsp::set_statsd_port(const uint16_t port)
} }
} }
void sinsp::add_plugin(std::shared_ptr<sinsp_plugin> plugin)
{
for(auto& it : m_plugins_list)
{
if(it->name() == plugin->name())
{
throw sinsp_exception("found multiple plugins with name " + it->name() + ". Aborting.");
}
}
m_plugins_list.push_back(plugin);
}
void sinsp::set_input_plugin(string plugin_name)
{
for(auto& it : m_plugins_list)
{
if(it->name() == plugin_name)
{
if(it->type() != TYPE_SOURCE_PLUGIN)
{
throw sinsp_exception("plugin " + plugin_name + " is not a source plugin and cannot be used as input.");
}
m_input_plugin = it;
return;
}
}
throw sinsp_exception("plugin " + plugin_name + " does not exist");
}
void sinsp::set_input_plugin_open_params(string params)
{
m_input_plugin_open_params = params;
}
const std::vector<std::shared_ptr<sinsp_plugin>>& sinsp::get_plugins()
{
return m_plugins_list;
}
std::shared_ptr<sinsp_plugin> sinsp::get_plugin_by_evt(sinsp_evt &evt)
{
//
// Only extract if the event is a plugin event.
//
if(evt.get_type() != PPME_PLUGINEVENT_E)
{
return std::shared_ptr<sinsp_plugin>();
}
sinsp_evt_param *parinfo;
parinfo = evt.get_param(0);
ASSERT(parinfo->m_len == sizeof(int32_t));
uint32_t pgid = *(int32_t *)parinfo->m_val;
return get_plugin_by_id(pgid);
}
std::shared_ptr<sinsp_plugin> sinsp::get_plugin_by_id(uint32_t plugin_id)
{
for(auto &it : m_plugins_list)
{
if(it->type() == TYPE_SOURCE_PLUGIN)
{
sinsp_source_plugin *splugin = static_cast<sinsp_source_plugin *>(it.get());
if(splugin->id() == plugin_id)
{
return it;
}
}
}
return std::shared_ptr<sinsp_plugin>();
}
std::shared_ptr<sinsp_plugin> sinsp::get_source_plugin_by_source(const std::string &source)
{
for(auto &it : m_plugins_list)
{
if(it->type() == TYPE_SOURCE_PLUGIN)
{
sinsp_source_plugin *splugin = static_cast<sinsp_source_plugin *>(it.get());
if(splugin->event_source() == source)
{
return it;
}
}
}
return std::shared_ptr<sinsp_plugin>();
}
void sinsp::stop_capture() void sinsp::stop_capture()
{ {
if(scap_stop_capture(m_h) != SCAP_SUCCESS) if(scap_stop_capture(m_h) != SCAP_SUCCESS)
@ -1707,13 +1882,12 @@ const unordered_map<uint32_t, scap_userinfo*>* sinsp::get_userlist()
scap_userinfo* sinsp::get_user(uint32_t uid) scap_userinfo* sinsp::get_user(uint32_t uid)
{ {
unordered_map<uint32_t, scap_userinfo*>::const_iterator it;
if(uid == 0xffffffff) if(uid == 0xffffffff)
{ {
return NULL; return NULL;
} }
it = m_userlist.find(uid); auto it = m_userlist.find(uid);
if(it == m_userlist.end()) if(it == m_userlist.end())
{ {
return NULL; return NULL;
@ -1727,13 +1901,29 @@ const unordered_map<uint32_t, scap_groupinfo*>* sinsp::get_grouplist()
return &m_grouplist; return &m_grouplist;
} }
scap_groupinfo* sinsp::get_group(uint32_t gid)
{
if(gid == 0xffffffff)
{
return NULL;
}
auto it = m_grouplist.find(gid);
if(it == m_grouplist.end())
{
return NULL;
}
return it->second;
}
#ifdef HAS_FILTERING #ifdef HAS_FILTERING
void sinsp::get_filtercheck_fields_info(OUT vector<const filter_check_info*>* list) void sinsp::get_filtercheck_fields_info(OUT vector<const filter_check_info*>& list)
{ {
sinsp_utils::get_filtercheck_fields_info(list); sinsp_utils::get_filtercheck_fields_info(list);
} }
#else #else
void sinsp::get_filtercheck_fields_info(OUT vector<const filter_check_info*>* list) void sinsp::get_filtercheck_fields_info(OUT vector<const filter_check_info*>& list)
{ {
} }
#endif #endif
@ -1921,7 +2111,7 @@ bool sinsp::setup_cycle_writer(string base_file_name, int rollover_mb, int durat
return m_cycle_writer->setup(base_file_name, rollover_mb, duration_seconds, file_limit, event_limit, &m_dumper); return m_cycle_writer->setup(base_file_name, rollover_mb, duration_seconds, file_limit, event_limit, &m_dumper);
} }
double sinsp::get_read_progress() double sinsp::get_read_progress_file()
{ {
if(m_input_fd != 0) if(m_input_fd != 0)
{ {
@ -1956,6 +2146,60 @@ void sinsp::set_metadata_download_params(uint32_t data_max_b,
m_metadata_download_params.m_data_watch_freq_sec = data_watch_freq_sec; m_metadata_download_params.m_data_watch_freq_sec = data_watch_freq_sec;
} }
void sinsp::get_read_progress_plugin(OUT double* nres, string* sres)
{
ASSERT(nres != NULL);
ASSERT(sres != NULL);
if(!nres || !sres)
{
return;
}
if (!m_input_plugin)
{
*nres = -1;
*sres = "No Input Plugin";
return;
}
sinsp_source_plugin *splugin = static_cast<sinsp_source_plugin *>(m_input_plugin.get());
uint32_t nplg;
*sres = splugin->get_progress(nplg);
*nres = ((double)nplg) / 100;
}
double sinsp::get_read_progress()
{
if(is_plugin())
{
double res = 0;
get_read_progress_plugin(&res, NULL);
return res;
}
else
{
return get_read_progress_file();
}
}
double sinsp::get_read_progress_with_str(OUT string* progress_str)
{
if(is_plugin())
{
double res;
get_read_progress_plugin(&res, progress_str);
return res;
}
else
{
*progress_str = "";
return get_read_progress_file();
}
}
bool sinsp::remove_inactive_threads() bool sinsp::remove_inactive_threads()
{ {
return m_thread_manager->remove_inactive_threads(); return m_thread_manager->remove_inactive_threads();

View File

@ -61,7 +61,7 @@ limitations under the License.
#include <map> #include <map>
#include <queue> #include <queue>
#include <vector> #include <vector>
#include <set> #include <unordered_set>
#include <list> #include <list>
#include <memory> #include <memory>
@ -89,6 +89,10 @@ using namespace std;
#define ONE_SECOND_IN_NS 1000000000LL #define ONE_SECOND_IN_NS 1000000000LL
#ifdef _WIN32
#define NOCURSESUI
#endif
#include "tuples.h" #include "tuples.h"
#include "fdinfo.h" #include "fdinfo.h"
#include "threadinfo.h" #include "threadinfo.h"
@ -97,6 +101,7 @@ using namespace std;
#include "sinsp_pd_callback_type.h" #include "sinsp_pd_callback_type.h"
#include "include/sinsp_external_processor.h" #include "include/sinsp_external_processor.h"
#include "plugin.h"
class sinsp_partial_transaction; class sinsp_partial_transaction;
class sinsp_parser; class sinsp_parser;
class sinsp_analyzer; class sinsp_analyzer;
@ -108,6 +113,7 @@ class k8s;
#endif // !defined(CYGWING_AGENT) && !defined(MINIMAL_BUILD) #endif // !defined(CYGWING_AGENT) && !defined(MINIMAL_BUILD)
class sinsp_partial_tracer; class sinsp_partial_tracer;
class mesos; class mesos;
class sinsp_plugin;
#if defined(HAS_CAPTURE) && !defined(_WIN32) #if defined(HAS_CAPTURE) && !defined(_WIN32)
class sinsp_ssl; class sinsp_ssl;
@ -139,6 +145,8 @@ public:
} }
string m_name; ///< Field class name. string m_name; ///< Field class name.
string m_shortdesc; ///< short (< 10 words) description of this filtercheck. Can be blank.
string m_desc; ///< Field class description.
int32_t m_nfields; ///< Number of fields in this field group. int32_t m_nfields; ///< Number of fields in this field group.
const filtercheck_field_info* m_fields; ///< Array containing m_nfields field descriptions. const filtercheck_field_info* m_fields; ///< Array containing m_nfields field descriptions.
uint32_t m_flags; uint32_t m_flags;
@ -450,7 +458,7 @@ public:
\brief Populate the given vector with the full list of filter check fields \brief Populate the given vector with the full list of filter check fields
that this version of the library supports. that this version of the library supports.
*/ */
static void get_filtercheck_fields_info(std::vector<const filter_check_info*>* list); static void get_filtercheck_fields_info(std::vector<const filter_check_info*>& list);
bool has_metrics(); bool has_metrics();
@ -520,6 +528,18 @@ public:
*/ */
const unordered_map<uint32_t, scap_groupinfo*>* get_grouplist(); const unordered_map<uint32_t, scap_groupinfo*>* get_grouplist();
/*!
\brief Lookup for group in the group table.
\return the \ref scap_groupinfo object containing full group information,
if group not found, returns NULL.
\note this call works with file captures as well, because the group
table is stored in the trace files. In that case, the returned
group list is the one of the machine where the capture happened.
*/
scap_groupinfo* get_group(uint32_t gid);
/*! /*!
\brief Fill the given structure with statistics about the currently \brief Fill the given structure with statistics about the currently
open capture. open capture.
@ -619,6 +639,22 @@ public:
return m_mode == SCAP_MODE_NODRIVER; return m_mode == SCAP_MODE_NODRIVER;
} }
/*!
\brief Returns true if the current capture has a plugin producing events
*/
inline bool is_plugin()
{
return m_mode == SCAP_MODE_PLUGIN;
}
/*!
\brief Returns the framework plugin api version as a string with static storage
*/
inline const char *get_plugin_api_version() const
{
return PLUGIN_API_VERSION_STR;
}
/*! /*!
\brief Returns true if truncated environments should be loaded from /proc \brief Returns true if truncated environments should be loaded from /proc
*/ */
@ -769,11 +805,18 @@ public:
void unset_eventmask(uint32_t event_id); void unset_eventmask(uint32_t event_id);
/*! /*!
\brief When reading events from a trace file, this function returns the \brief When reading events from a trace file or a plugin, this function
read progress as a number between 0 and 100. returns the read progress as a number between 0 and 100.
*/ */
double get_read_progress(); double get_read_progress();
/*!
\brief When reading events from a trace file or a plugin, this function
returns the read progress as a number and as a string, giving the plugins
flexibility on the format.
*/
double get_read_progress_with_str(OUT string* progress_str);
/*! /*!
\brief Make the amount of data gathered for a syscall to be \brief Make the amount of data gathered for a syscall to be
determined by the number of parameters. determined by the number of parameters.
@ -847,6 +890,13 @@ public:
sinsp_parser* get_parser(); sinsp_parser* get_parser();
/*!
\brief Enables simple_consumer mode on sinsp, at driver level.
This will avoid tracing syscalls flagged with EF_DROP_SIMPLE_CONS.
Must be called before sinsp opening.
*/
void set_simple_consumer();
bool setup_cycle_writer(std::string base_file_name, int rollover_mb, int duration_seconds, int file_limit, unsigned long event_limit, bool compress); bool setup_cycle_writer(std::string base_file_name, int rollover_mb, int duration_seconds, int file_limit, unsigned long event_limit, bool compress);
void import_ipv4_interface(const sinsp_ipv4_ifinfo& ifinfo); void import_ipv4_interface(const sinsp_ipv4_ifinfo& ifinfo);
void add_meta_event(sinsp_evt *metaevt); void add_meta_event(sinsp_evt *metaevt);
@ -916,6 +966,14 @@ public:
void set_cri_delay(uint64_t delay_ms); void set_cri_delay(uint64_t delay_ms);
void set_container_labels_max_len(uint32_t max_label_len); void set_container_labels_max_len(uint32_t max_label_len);
void add_plugin(std::shared_ptr<sinsp_plugin> plugin);
void set_input_plugin(string plugin_name);
void set_input_plugin_open_params(string params);
const std::vector<std::shared_ptr<sinsp_plugin>>& get_plugins();
std::shared_ptr<sinsp_plugin> get_plugin_by_evt(sinsp_evt &evt);
std::shared_ptr<sinsp_plugin> get_plugin_by_id(uint32_t plugin_id);
std::shared_ptr<sinsp_plugin> get_source_plugin_by_source(const std::string &source);
uint64_t get_lastevent_ts() const { return m_lastevent_ts; } uint64_t get_lastevent_ts() const { return m_lastevent_ts; }
VISIBILITY_PROTECTED VISIBILITY_PROTECTED
@ -929,7 +987,7 @@ VISIBILITY_PRIVATE
static inline ppm_event_flags simple_consumer_skip_flags() static inline ppm_event_flags simple_consumer_skip_flags()
{ {
return (ppm_event_flags) (EF_SKIPPARSERESET | EF_UNUSED | EF_DROP_SIMPLE_CONS); return (ppm_event_flags) (EF_SKIPPARSERESET | EF_UNUSED | EF_DROP_SIMPLE_CONS | EF_OLD_VERSION);
} }
// Doxygen doesn't understand VISIBILITY_PRIVATE // Doxygen doesn't understand VISIBILITY_PRIVATE
#ifdef _DOXYGEN #ifdef _DOXYGEN
@ -939,11 +997,12 @@ private:
void open_int(); void open_int();
void open_live_common(uint32_t timeout_ms, scap_mode_t mode); void open_live_common(uint32_t timeout_ms, scap_mode_t mode);
void init(); void init();
void deinit_state();
void import_thread_table(); void import_thread_table();
void import_ifaddr_list(); void import_ifaddr_list();
void import_user_list(); void import_user_list();
void add_protodecoders(); void add_protodecoders();
void fill_syscalls_of_interest(scap_open_args *oargs);
void remove_thread(int64_t tid, bool force); void remove_thread(int64_t tid, bool force);
// //
@ -972,7 +1031,7 @@ private:
static int64_t get_file_size(const std::string& fname, char *error); static int64_t get_file_size(const std::string& fname, char *error);
static std::string get_error_desc(const std::string& msg = ""); static std::string get_error_desc(const std::string& msg = "");
void restart_capture_at_filepos(uint64_t filepos); void restart_capture();
void fseek(uint64_t filepos) void fseek(uint64_t filepos)
{ {
@ -987,12 +1046,17 @@ private:
m_increased_snaplen_port_range.range_end > 0; m_increased_snaplen_port_range.range_end > 0;
} }
double get_read_progress_file();
void get_read_progress_plugin(OUT double* nres, string* sres);
void get_procs_cpu_from_driver(uint64_t ts); void get_procs_cpu_from_driver(uint64_t ts);
scap_t* m_h; scap_t* m_h;
uint32_t m_nevts; uint64_t m_nevts;
int64_t m_filesize; int64_t m_filesize;
bool m_simpleconsumer;
scap_mode_t m_mode = SCAP_MODE_NONE; scap_mode_t m_mode = SCAP_MODE_NONE;
// If non-zero, reading from this fd and m_input_filename contains "fd // If non-zero, reading from this fd and m_input_filename contains "fd
@ -1026,9 +1090,6 @@ private:
uint32_t m_num_cpus; uint32_t m_num_cpus;
sinsp_thread_privatestate_manager m_thread_privatestate_manager; sinsp_thread_privatestate_manager m_thread_privatestate_manager;
bool m_is_tracers_capture_enabled; bool m_is_tracers_capture_enabled;
// This is used to support reading merged files, where the capture needs to
// restart in the middle of the file.
uint64_t m_file_start_offset;
bool m_flush_memory_dump; bool m_flush_memory_dump;
bool m_large_envs_enabled; bool m_large_envs_enabled;
@ -1087,8 +1148,8 @@ public:
sinsp_filter* m_filter; sinsp_filter* m_filter;
sinsp_evttype_filter *m_evttype_filter; sinsp_evttype_filter *m_evttype_filter;
std::string m_filterstring; std::string m_filterstring;
#endif #endif
unordered_set<uint32_t> m_ppm_sc_of_interest;
// //
// Internal stats // Internal stats
@ -1194,13 +1255,38 @@ public:
static unsigned int m_num_possible_cpus; static unsigned int m_num_possible_cpus;
#if defined(HAS_CAPTURE) #if defined(HAS_CAPTURE)
int64_t m_sysdig_pid; int64_t m_self_pid;
#endif #endif
// Any thread with a comm in this set will not have its events // Any thread with a comm in this set will not have its events
// returned in sinsp::next() // returned in sinsp::next()
std::set<std::string> m_suppressed_comms; std::set<std::string> m_suppressed_comms;
//
// List of the sinsp/scap plugins configured by the user.
//
std::vector<std::shared_ptr<sinsp_plugin>> m_plugins_list;
//
// The ID of the plugin to use as event input, or zero
// if no source plugin should be used as source
//
std::shared_ptr<sinsp_plugin> m_input_plugin;
//
// String with the parameters for the plugin to be used as input.
// These parameters will be passed to the open function of the plugin.
//
string m_input_plugin_open_params;
//
// An instance of scap_evt to be used during the next call to sinsp::next().
// If non-null, sinsp::next will use this pointer instead of invoking scap_next().
// After using this event, sinsp::next() will set this back to NULL.
// This is used internally during the state initialization phase.
scap_evt *m_replay_scap_evt;
//
// This is related to m_replay_scap_evt, and is used to store the additional cpuid
// information of the replayed scap event.
uint16_t m_replay_scap_cpuid;
friend class sinsp_parser; friend class sinsp_parser;
friend class sinsp_analyzer; friend class sinsp_analyzer;
friend class sinsp_analyzer_parsers; friend class sinsp_analyzer_parsers;

View File

@ -239,8 +239,7 @@ size_t sinsp_curl::header_callback(char *buffer, size_t size, size_t nitems, voi
if(sz < CURL_MAX_HTTP_HEADER) if(sz < CURL_MAX_HTTP_HEADER)
{ {
g_logger.log("HTTP redirect Location: (" + buf + ')', sinsp_logger::SEV_TRACE); g_logger.log("HTTP redirect Location: (" + buf + ')', sinsp_logger::SEV_TRACE);
std::strncpy((char*) userdata, buf.data(), sz); strlcpy((char*) userdata, buf.c_str(), CURL_MAX_HTTP_HEADER);
((char*) userdata)[sz] = 0;
} }
} }
return nitems * size; return nitems * size;

View File

@ -353,47 +353,73 @@ public:
} }
} }
int get_all_data() int get_all_data_secure(std::vector<char> &buf)
{ {
g_logger.log("Socket handler (" + m_id + ") Retrieving all data in blocking mode ...", int processed = 0;
sinsp_logger::SEV_TRACE); int rec = SSL_read(m_ssl_connection, &buf[0], buf.size());
ssize_t rec = 0; if (rec > 0)
std::vector<char> buf(1024, 0);
int counter = 0;
uint32_t processed = 0;
init_http_parser();
do
{ {
processed += rec;
}
int err = SSL_get_error(m_ssl_connection, rec);
switch (err) {
case SSL_ERROR_NONE:
process(&buf[0], rec, false);
break;
case SSL_ERROR_ZERO_RETURN:
throw sinsp_exception("SSL Socket handler (" + m_id + "): Connection closed.");
break;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
break;
default:
g_logger.log("SSL Socket handler (" + m_id + ") received=" + std::to_string(err) + " SSL error.", sinsp_logger::SEV_TRACE);
break;
}
return processed;
}
int get_all_data_unsecure(std::vector<char> &buf) {
int rec;
int processed = 0;
int count = 0; int count = 0;
int ioret = ioctl(m_socket, FIONREAD, &count); int ioret = ioctl(m_socket, FIONREAD, &count);
if(ioret >= 0 && count > 0) if(ioret >= 0 && count > 0)
{ {
buf.resize(count); buf.resize(count);
if(m_url.is_secure()) rec = recv(m_socket, &buf[0], count, 0);
{ switch (rec) {
rec = SSL_read(m_ssl_connection, &buf[0], buf.size()); case 0:
}
else
{
rec = recv(m_socket, &buf[0], buf.size(), 0);
}
if(rec > 0)
{
process(&buf[0], rec, false);
processed += (uint32_t)rec;
}
else if(rec == 0)
{
throw sinsp_exception("Socket handler (" + m_id + "): Connection closed."); throw sinsp_exception("Socket handler (" + m_id + "): Connection closed.");
} break;
else if(rec < 0) case -1:
{
throw sinsp_exception("Socket handler (" + m_id + "): " + strerror(errno)); throw sinsp_exception("Socket handler (" + m_id + "): " + strerror(errno));
break;
default:
process(&buf[0], rec, false);
processed = rec;
break;
} }
//g_logger.log("Socket handler (" + m_id + ") received=" + std::to_string(rec) + }
// "\n\n" + data + "\n\n", sinsp_logger::SEV_TRACE); return processed;
} }
int get_all_data()
{
int processed = 0;
int counter = 0;
std::vector<char> buf(1024, 0);
g_logger.log("Socket handler (" + m_id + ") Retrieving all data in blocking mode ...",
sinsp_logger::SEV_TRACE);
init_http_parser();
do
{
if (m_url.is_secure()) {
processed += get_all_data_secure(buf);
} else {
processed += get_all_data_unsecure(buf);
}
// To prevent reads from entirely stalling (like in gigantic k8s environments), // To prevent reads from entirely stalling (like in gigantic k8s environments),
// give up after reading a certain size (by default, 100MB, but configurable). // give up after reading a certain size (by default, 100MB, but configurable).
++counter; ++counter;
@ -403,8 +429,9 @@ public:
"read more than " + to_string(m_data_max_b / 1024 / 1024) + " MB of data from " + "read more than " + to_string(m_data_max_b / 1024 / 1024) + " MB of data from " +
m_url.to_string(false) + m_path + " (" + std::to_string(processed) + m_url.to_string(false) + m_path + " (" + std::to_string(processed) +
" bytes, " + std::to_string(counter) + " reads). Giving up"); " bytes, " + std::to_string(counter) + " reads). Giving up");
} else {
usleep(m_data_chunk_wait_us);
} }
else { usleep(m_data_chunk_wait_us); }
} while(!m_msg_completed); } while(!m_msg_completed);
init_http_parser(); init_http_parser();
return processed; return processed;
@ -923,7 +950,7 @@ private:
std::memset(buf, 0, size); std::memset(buf, 0, size);
int pass_len = static_cast<int>(strlen((char*)pass)); int pass_len = static_cast<int>(strlen((char*)pass));
if(size < (pass_len) + 1) { return 0; } if(size < (pass_len) + 1) { return 0; }
strncpy(buf, (const char*)pass, pass_len); strlcpy(buf, (const char*)pass, size);
return pass_len; return pass_len;
} }
return 0; return 0;
@ -1382,8 +1409,7 @@ private:
throw sinsp_exception("Invalid address (too long): [" + m_url.get_path() + ']'); throw sinsp_exception("Invalid address (too long): [" + m_url.get_path() + ']');
} }
m_file_addr.sun_family = AF_UNIX; m_file_addr.sun_family = AF_UNIX;
strncpy(m_file_addr.sun_path, m_url.get_path().c_str(), m_url.get_path().length()); strlcpy(m_file_addr.sun_path, m_url.get_path().c_str(), sizeof(m_file_addr.sun_path));
m_file_addr.sun_path[sizeof(m_file_addr.sun_path) - 1]= '\0';
m_sa = (sockaddr*)&m_file_addr; m_sa = (sockaddr*)&m_file_addr;
m_sa_len = sizeof(struct sockaddr_un); m_sa_len = sizeof(struct sockaddr_un);
} }

Some files were not shown because too many files have changed in this diff Show More