From 38f0bed52b412a89ae6669a478805c8f91af0ff9 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Mon, 20 Nov 2023 19:30:50 +0100 Subject: [PATCH 1/5] tracker-extract: Limit filesystem access through Landlock Landlock is a relatively recent kernel API (since 5.3) that allows setting up high level access rules to specific portions of the filesystem. Use this API if available when spawning tracker-extract-3, in order to avoid any possible extraneous access outside of our specified locations. The following locations have some form of read permissions: - The indexed folders, as the source of all file data extracted. The metadata extractor not only opens files directly, it also may wants to open "indirect" files like CUE sheets, XMP sidecar files, etc. Thus access to full directories is allowed. - The library locations, in the installed prefix, extended to /usr if the prefixes mismatch, and LD_LIBRARY_PATH if set - The data locations, in the installed prefix, extended to /usr if the prefixes mismatch. - Some misc specific files (/etc/passwd, /proc/mounts) for correct GIO volume monitor behavior. With all other operations supported by Landlock being disallowed, plus read operations in other directories outside this set. This is a protection layer meant to complement seccomp, which is still the primary form of protection since it restricts all write access to the filesystem, besides other things like opening sockets. Anyhow, there's still some chance that read only access might cause some mischief (e.g. fiddling in /proc). Still, treat this as a nicety, and don't warn if the feature is disabled (e.g. old kernels), like we do with seccomp. --- config-miners.h.meson.in | 3 + meson.build | 8 + src/libtracker-miners-common/meson.build | 12 +- src/libtracker-miners-common/tracker-common.h | 5 + .../tracker-landlock.c | 305 ++++++++++++++++++ .../tracker-landlock.h | 29 ++ src/miners/fs/tracker-extract-watchdog.c | 55 +++- src/miners/fs/tracker-extract-watchdog.h | 5 +- src/miners/fs/tracker-miner-files.c | 3 +- 9 files changed, 421 insertions(+), 4 deletions(-) create mode 100644 src/libtracker-miners-common/tracker-landlock.c create mode 100644 src/libtracker-miners-common/tracker-landlock.h diff --git a/config-miners.h.meson.in b/config-miners.h.meson.in index 48fa769b0..b6417181e 100644 --- a/config-miners.h.meson.in +++ b/config-miners.h.meson.in @@ -80,6 +80,9 @@ /* Define if btrfs has the necessary ioctl()s */ #mesondefine HAVE_BTRFS_IOCTL +/* Define if landlock is available */ +#mesondefine HAVE_LANDLOCK + /* Define to the address where bug reports for this package should be sent. */ #mesondefine PACKAGE_BUGREPORT diff --git a/meson.build b/meson.build index 415538614..c2458c2bc 100644 --- a/meson.build +++ b/meson.build @@ -181,6 +181,12 @@ else have_btrfs_ioctl = false endif +########################################## +# Check for landlock +########################################## + +have_landlock = cc.has_header('linux/landlock.h') + #################################################################### # This section is for tracker-extract dependencies #################################################################### @@ -367,6 +373,7 @@ conf.set('HAVE_POSIX_FADVISE', cc.has_function('posix_fadvise', prefix : '#inclu conf.set('HAVE_STATVFS64', cc.has_header_symbol('sys/statvfs.h', 'statvfs64', args: '-D_LARGEFILE64_SOURCE')) conf.set('HAVE_STRNLEN', cc.has_function('strnlen', prefix : '#include ')) conf.set('HAVE_MEMFD_CREATE', cc.has_function('memfd_create', prefix : '#define _GNU_SOURCE\n#include ')) +conf.set('HAVE_LANDLOCK', have_landlock) conf.set_quoted('LOCALEDIR', get_option('prefix') / get_option('localedir')) conf.set_quoted('SHAREDIR', get_option('prefix') / get_option('datadir')) @@ -479,6 +486,7 @@ summary = [ ' Domain prefix: ' + get_option('domain_prefix'), '\nFeature Support:', ' File monitoring: @0@glib'.format(have_fanotify ? 'fanotify ' : ''), + ' Landlock: ' + have_landlock.to_string(), ' BTRFS subvolumes: ' + have_btrfs_ioctl.to_string(), ' Battery/mains power detection: ' + battery_detection_library_name, ' Support for network status detection: ' + have_network_manager.to_string(), diff --git a/src/libtracker-miners-common/meson.build b/src/libtracker-miners-common/meson.build index 22026bbc0..cc570a7c6 100644 --- a/src/libtracker-miners-common/meson.build +++ b/src/libtracker-miners-common/meson.build @@ -25,6 +25,10 @@ tracker_miners_common_sources = [ enums[0], enums[1], ] +if have_landlock + tracker_miners_common_sources += 'tracker-landlock.c' +endif + tracker_miners_common_dependencies = [glib, gio, gio_unix, libmath, tracker_sparql] if battery_detection_library_name == 'upower' @@ -49,7 +53,13 @@ libtracker_miners_common = static_library('tracker-miners-common', tracker_miners_common_sources, dependencies: tracker_miners_common_dependencies + tracker_miners_common_private_dependencies, c_args: tracker_c_args + [ - '-DTRACKERSHAREDIR="@0@"'.format(tracker_versioned_name), + '-DTRACKERSHAREDIR="@0@"'.format(tracker_versioned_name), + '-DLIBDIR="@0@"'.format(get_option ('libdir')), + '-DDATADIR="@0@"'.format(get_option ('datadir')), + '-DPREFIX="@0@"'.format(get_option ('prefix')), + '-DBUILDROOT="@0@"'.format(meson.build_root()), + '-DSRCROOT="@0@"'.format(meson.source_root()), + '-DLIBEXECDIR="@0@"'.format(join_paths(get_option('prefix'), get_option('libexecdir'))), ], include_directories: [configinc, srcinc], ) diff --git a/src/libtracker-miners-common/tracker-common.h b/src/libtracker-miners-common/tracker-common.h index 7da2b551c..765f9fb05 100644 --- a/src/libtracker-miners-common/tracker-common.h +++ b/src/libtracker-miners-common/tracker-common.h @@ -40,6 +40,11 @@ #include "tracker-file-utils.h" #include "tracker-fts-config.h" #include "tracker-ioprio.h" + +#ifdef HAVE_LANDLOCK +#include "tracker-landlock.h" +#endif + #include "tracker-miner.h" #include "tracker-miner-proxy.h" #include "tracker-sched.h" diff --git a/src/libtracker-miners-common/tracker-landlock.c b/src/libtracker-miners-common/tracker-landlock.c new file mode 100644 index 000000000..3af08ef65 --- /dev/null +++ b/src/libtracker-miners-common/tracker-landlock.c @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2023, Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * Author: Carlos Garnacho + */ + +#include "config-miners.h" + +#include "tracker-landlock.h" + +#include +#include +#include +#include +#include + +/* Compensate for these syscalls not being wrapped in libc */ +#define CREATE_RULESET(attr, flags) \ + syscall (SYS_landlock_create_ruleset, (attr), \ + (attr) != NULL ? sizeof *(attr) : 0, \ + flags) + +#define ADD_RULE(fd, type, attr, flags) \ + syscall (SYS_landlock_add_rule, fd, type, (attr), flags) + +#define RESTRICT_SELF(fd, flags) \ + syscall (SYS_landlock_restrict_self, fd, flags) + +/* Cautious defines of flags in newer ABI versions */ +#ifndef LANDLOCK_ACCESS_FS_REFER +#define LANDLOCK_ACCESS_FS_REFER (1ULL << 13) +#endif + +#ifndef LANDLOCK_ACCESS_FS_TRUNCATE +#define LANDLOCK_ACCESS_FS_TRUNCATE (1ULL << 14) +#endif + +typedef struct _TrackerLandlockRule TrackerLandlockRule; + +struct _TrackerLandlockRule +{ + const gchar *path; + guint64 flags; +}; + +gboolean +get_supported_fs_flags (guint32 *flags_out) +{ + guint64 supported_abi_flags[] = { + /* Version 1 */ + (LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_WRITE_FILE | + LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_READ_DIR | + LANDLOCK_ACCESS_FS_REMOVE_DIR | + LANDLOCK_ACCESS_FS_REMOVE_FILE | + LANDLOCK_ACCESS_FS_MAKE_CHAR | + LANDLOCK_ACCESS_FS_MAKE_DIR | + LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_MAKE_SOCK | + LANDLOCK_ACCESS_FS_MAKE_FIFO | + LANDLOCK_ACCESS_FS_MAKE_BLOCK | + LANDLOCK_ACCESS_FS_MAKE_SYM), + /* Version 2 */ + LANDLOCK_ACCESS_FS_REFER, + /* Version 3 */ + LANDLOCK_ACCESS_FS_TRUNCATE, + }; + guint32 flags = 0; + int i, abi; + + abi = CREATE_RULESET (NULL, LANDLOCK_CREATE_RULESET_VERSION); + if (abi < 0) { + g_critical ("Could not get landlock supported ABI: %m"); + return FALSE; + } + + for (i = 0; i < MIN (G_N_ELEMENTS (supported_abi_flags), abi); i++) + flags |= supported_abi_flags[i]; + + *flags_out = flags; + + return TRUE; +} + +static void +add_rule (int landlock_fd, + const gchar *path, + guint64 flags) +{ + struct landlock_path_beneath_attr attr = { 0, }; + int fd; + int result; + + if (!g_file_test (path, G_FILE_TEST_EXISTS)) { + g_debug ("Path %s does not exist in filesystem", path); + return; + } + + fd = open (path, O_PATH | O_CLOEXEC); + if (fd < 0) { + g_warning ("Could not open '%s' to apply landlock rules: %m", path); + return; + } + + attr = (struct landlock_path_beneath_attr) { + .allowed_access = flags, + .parent_fd = fd, + }; + + result = ADD_RULE (landlock_fd, LANDLOCK_RULE_PATH_BENEATH, &attr, 0); + close(fd); + if (result != 0) { + g_warning ("Could not add landlock rule for '%s': %m", path); + return; + } +} + +static gboolean +create_ruleset (int *landlock_fd) +{ + struct landlock_ruleset_attr attr; + int fd; + guint32 flags; + + /* Get supported flags per the landlock ABI available */ + if (!get_supported_fs_flags (&flags)) + return FALSE; + + /* Create ruleset */ + attr = (struct landlock_ruleset_attr) { + .handled_access_fs = flags, + }; + + fd = CREATE_RULESET (&attr, 0); + if (landlock_fd < 0) { + g_critical ("Failed to create landlock ruleset: %m"); + return FALSE; + } + + *landlock_fd = fd; + + return TRUE; +} + +static gboolean +apply_ruleset (int landlock_fd) +{ + /* Restrict any future new permission, necessary for the next step */ + if (prctl (PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) { + g_critical ("Failed to restrict new privileges: %m"); + return FALSE; + } + + if (RESTRICT_SELF (landlock_fd, 0) != 0) { + g_critical ("Failed to apply landlock ruleset: %m"); + return FALSE; + } + + return TRUE; +} + +gboolean +tracker_landlock_init (const gchar * const *indexed_folders) +{ + TrackerLandlockRule stock_rules[] = { + /* Allow access to the executable itself */ + { LIBEXECDIR "/tracker-extract-3", + LANDLOCK_ACCESS_FS_EXECUTE }, + /* Library dirs, as we shockingly use libraries. Extends to /usr */ + { PREFIX "/" LIBDIR, + (LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_READ_DIR) }, +#if INTPTR_MAX == INT64_MAX + { "/usr/lib64", + (LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_READ_DIR) }, +#endif + { "/usr/lib", + (LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_READ_DIR) }, + /* Data dir, to access miscellaneous files. Extends to /usr */ + { PREFIX "/" DATADIR, + (LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_READ_DIR) }, + { "/usr/share", + (LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_READ_DIR) }, + /* Necessary for libosinfo in Ubuntu/Debian */ + { "/var/lib/usbutils", + LANDLOCK_ACCESS_FS_READ_FILE }, + /* Necessary for mount monitors */ + { "/proc/mounts", + LANDLOCK_ACCESS_FS_READ_FILE }, + { "/proc/self/mountinfo", + LANDLOCK_ACCESS_FS_READ_FILE }, + /* Necessary for g_get_user_name() */ + { "/etc/passwd", + LANDLOCK_ACCESS_FS_READ_FILE }, + }; + TrackerLandlockRule homedir_rules[] = { + /* Disable file access to sensitive folders the extractor has + * no business with. Since a flag bit needs to be set, only + * allow dir read access. + */ + { ".ssh", LANDLOCK_ACCESS_FS_READ_DIR }, + { ".pki", LANDLOCK_ACCESS_FS_READ_DIR }, + { ".gnupg", LANDLOCK_ACCESS_FS_READ_DIR }, + }; + g_autofree gchar *current_dir = NULL, *cache_dir = NULL; + g_auto (GStrv) library_paths = NULL; + const gchar *ld_library_path = NULL; + int i, landlock_fd; + gboolean retval; + + if (!create_ruleset (&landlock_fd)) + return FALSE; + + /* Populate ruleset */ + for (i = 0; i < G_N_ELEMENTS (stock_rules); i++) { + add_rule (landlock_fd, stock_rules[i].path, + stock_rules[i].flags); + } + + for (i = 0; i < G_N_ELEMENTS (homedir_rules); i++) { + g_autofree gchar *homedir_path = NULL; + + homedir_path = g_build_filename (g_get_home_dir (), + homedir_rules[i].path, NULL); + add_rule (landlock_fd, homedir_path, homedir_rules[i].flags); + } + + for (i = 0; indexed_folders && indexed_folders[i]; i++) { + add_rule (landlock_fd, + indexed_folders[i], + LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_READ_DIR); + } + + /* Cater for development environments */ + ld_library_path = g_getenv ("LD_LIBRARY_PATH"); + if (ld_library_path) { + library_paths = g_strsplit (ld_library_path, ":", -1); + for (i = 0; library_paths && library_paths[i]; i++) { + add_rule (landlock_fd, + library_paths[i], + LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_READ_DIR); + } + } + + current_dir = g_get_current_dir (); + + /* Detect running in-tree */ + if (g_strcmp0 (current_dir, BUILDROOT) == 0) { + TrackerLandlockRule in_tree_rules[] = { + { BUILDROOT, + (LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_READ_DIR | + LANDLOCK_ACCESS_FS_EXECUTE) }, + { SRCROOT, + (LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_READ_DIR) }, + }; + + for (i = 0; i < G_N_ELEMENTS (in_tree_rules); i++) { + add_rule (landlock_fd, in_tree_rules[i].path, + in_tree_rules[i].flags); + } + } + + /* Add user cache for readonly databases */ +#ifdef MINER_FS_CACHE_LOCATION + add_rule (landlock_fd, MINER_FS_CACHE_LOCATION, + LANDLOCK_ACCESS_FS_READ_FILE); +#else + cache_dir = g_build_filename (g_get_user_cache_dir (), "tracker3", "files", NULL); + add_rule (landlock_fd, cache_dir, + LANDLOCK_ACCESS_FS_READ_FILE); +#endif + + retval = apply_ruleset (landlock_fd); + close (landlock_fd); + + return retval; +} diff --git a/src/libtracker-miners-common/tracker-landlock.h b/src/libtracker-miners-common/tracker-landlock.h new file mode 100644 index 000000000..dbb4f8b00 --- /dev/null +++ b/src/libtracker-miners-common/tracker-landlock.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2023, Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * Author: Carlos Garnacho + */ + +#ifndef __TRACKER_LANDLOCK_H__ +#define __TRACKER_LANDLOCK_H__ + +#include + +gboolean tracker_landlock_init (const gchar * const *allowed_paths); + +#endif /* __TRACKER_LANDLOCK_H__ */ diff --git a/src/miners/fs/tracker-extract-watchdog.c b/src/miners/fs/tracker-extract-watchdog.c index da7d74ef6..ccf89c946 100644 --- a/src/miners/fs/tracker-extract-watchdog.c +++ b/src/miners/fs/tracker-extract-watchdog.c @@ -40,6 +40,7 @@ static guint signals[N_SIGNALS] = { 0, }; enum { PROP_0, PROP_SPARQL_CONN, + PROP_INDEXING_TREE, N_PROPS, }; @@ -54,6 +55,7 @@ struct _TrackerExtractWatchdog { GDBusConnection *conn; TrackerEndpoint *endpoint; TrackerFilesInterface *files_interface; + TrackerIndexingTree *indexing_tree; guint progress_signal_id; guint error_signal_id; int persistence_fd; @@ -161,6 +163,9 @@ tracker_extract_watchdog_finalize (GObject *object) if (watchdog->persistence_fd) close (watchdog->persistence_fd); + g_clear_object (&watchdog->sparql_conn); + g_clear_object (&watchdog->indexing_tree); + G_OBJECT_CLASS (tracker_extract_watchdog_parent_class)->finalize (object); } @@ -176,6 +181,9 @@ tracker_extract_watchdog_set_property (GObject *object, case PROP_SPARQL_CONN: watchdog->sparql_conn = g_value_dup_object (value); break; + case PROP_INDEXING_TREE: + watchdog->indexing_tree = g_value_dup_object (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -211,6 +219,12 @@ tracker_extract_watchdog_class_init (TrackerExtractWatchdogClass *klass) G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + props[PROP_INDEXING_TREE] = + g_param_spec_object ("indexing-tree", NULL, NULL, + TRACKER_TYPE_INDEXING_TREE, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, N_PROPS, props); } @@ -223,10 +237,12 @@ tracker_extract_watchdog_init (TrackerExtractWatchdog *watchdog) } TrackerExtractWatchdog * -tracker_extract_watchdog_new (TrackerSparqlConnection *sparql_conn) +tracker_extract_watchdog_new (TrackerSparqlConnection *sparql_conn, + TrackerIndexingTree *indexing_tree) { return g_object_new (TRACKER_TYPE_EXTRACT_WATCHDOG, "sparql-conn", sparql_conn, + "indexing-tree", indexing_tree, NULL); } @@ -317,6 +333,38 @@ wait_check_async_cb (GObject *object, g_signal_emit (watchdog, signals[STATUS], 0, "Idle", 1.0, 0); } +static GStrv +get_indexed_folders (TrackerExtractWatchdog *watchdog) +{ + GArray *array; + GList *roots, *l; + + array = g_array_new (TRUE, FALSE, sizeof (gchar*)); + roots = tracker_indexing_tree_list_roots (watchdog->indexing_tree); + + for (l = roots; l; l = l->next) { + GFile *file = l->data; + gchar *path = NULL; + + path = g_file_get_path (file); + if (path) + g_array_append_val (array, path); + } + + return (GStrv) g_array_free (array, FALSE); +} + +static void +extractor_child_setup (gpointer user_data) +{ +#ifdef HAVE_LANDLOCK + const gchar * const *indexed_folders = user_data; + + if (!tracker_landlock_init (indexed_folders)) + g_assert_not_reached (); +#endif +} + static gboolean setup_context (TrackerExtractWatchdog *watchdog, GError **error) @@ -341,6 +389,11 @@ setup_context (TrackerExtractWatchdog *watchdog, watchdog->launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE); g_subprocess_launcher_take_fd (watchdog->launcher, fd_pair[1], REMOTE_FD_NUMBER); + g_subprocess_launcher_set_child_setup (watchdog->launcher, + extractor_child_setup, + get_indexed_folders (watchdog), + (GDestroyNotify) g_strfreev); + socket = g_socket_new_from_fd (fd_pair[0], error); if (!socket) { close (fd_pair[0]); diff --git a/src/miners/fs/tracker-extract-watchdog.h b/src/miners/fs/tracker-extract-watchdog.h index 64e8e125e..0efbb41ad 100644 --- a/src/miners/fs/tracker-extract-watchdog.h +++ b/src/miners/fs/tracker-extract-watchdog.h @@ -22,6 +22,8 @@ #include +#include "tracker-indexing-tree.h" + G_BEGIN_DECLS #define TRACKER_TYPE_EXTRACT_WATCHDOG (tracker_extract_watchdog_get_type ()) @@ -31,7 +33,8 @@ G_DECLARE_FINAL_TYPE (TrackerExtractWatchdog, TRACKER, EXTRACT_WATCHDOG, GObject) -TrackerExtractWatchdog * tracker_extract_watchdog_new (TrackerSparqlConnection *sparql_conn); +TrackerExtractWatchdog * tracker_extract_watchdog_new (TrackerSparqlConnection *sparql_conn, + TrackerIndexingTree *indexing_tree); void tracker_extract_watchdog_ensure_started (TrackerExtractWatchdog *watchdog); diff --git a/src/miners/fs/tracker-miner-files.c b/src/miners/fs/tracker-miner-files.c index e254d281e..a25295d7e 100644 --- a/src/miners/fs/tracker-miner-files.c +++ b/src/miners/fs/tracker-miner-files.c @@ -1072,7 +1072,8 @@ miner_files_constructed (GObject *object) domain_name = tracker_domain_ontology_get_domain (mf->private->domain_ontology, NULL); mf->private->extract_watchdog = - tracker_extract_watchdog_new (tracker_miner_get_connection (TRACKER_MINER (mf))); + tracker_extract_watchdog_new (tracker_miner_get_connection (TRACKER_MINER (mf)), + tracker_miner_fs_get_indexing_tree (TRACKER_MINER_FS (mf))); g_signal_connect (mf->private->extract_watchdog, "lost", G_CALLBACK (on_extractor_lost), mf); g_signal_connect (mf->private->extract_watchdog, "status", -- GitLab From c8cf3bb34fb8ab0fd23003ca4859fee62eb0ae5d Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sat, 9 Dec 2023 11:32:45 +0100 Subject: [PATCH 2/5] cli: Landlock "tracker3 extract" subprocess --- src/tracker/tracker-extract.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/tracker/tracker-extract.c b/src/tracker/tracker-extract.c index 364755789..8d5913290 100644 --- a/src/tracker/tracker-extract.c +++ b/src/tracker/tracker-extract.c @@ -47,6 +47,18 @@ static GOptionEntry entries[] = { { NULL } }; +static void +extractor_child_setup (gpointer user_data) +{ +#ifdef HAVE_LANDLOCK + g_autofree gchar *folder = NULL; + + folder = g_path_get_dirname (user_data); + + if (!tracker_landlock_init ((const gchar*[]) { folder, NULL })) + g_assert_not_reached (); +#endif +} static gint extract_files (char *output_format) @@ -69,7 +81,9 @@ extract_files (char *output_format) "--output-format", output_format, "--file", *p, NULL }; - g_spawn_sync(NULL, argv, NULL, G_SPAWN_DEFAULT, NULL, NULL, NULL, NULL, NULL, &error); + g_spawn_sync (NULL, argv, NULL, G_SPAWN_DEFAULT, + extractor_child_setup, *p, + NULL, NULL, NULL, &error); if (error) { g_printerr ("%s: %s\n", -- GitLab From 3b74bd219b37a6b3b0e41afd65b03c4bfe405545 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sat, 2 Dec 2023 00:18:02 +0100 Subject: [PATCH 3/5] build: Add build-time option for Landlock We won't want to disable it often, but offer the off switch for the cases it might be needed. --- meson.build | 12 +++++++++--- meson_options.txt | 4 +++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/meson.build b/meson.build index c2458c2bc..d3d280788 100644 --- a/meson.build +++ b/meson.build @@ -185,7 +185,7 @@ endif # Check for landlock ########################################## -have_landlock = cc.has_header('linux/landlock.h') +have_landlock = cc.has_header('linux/landlock.h', required: get_option('landlock')) #################################################################### # This section is for tracker-extract dependencies @@ -541,6 +541,12 @@ endif message('\n'.join(summary)) -if not get_option('seccomp') - warning('Sandboxing is disabled. Run at your own risk. Distribution is discouraged.') +if not get_option('seccomp') or get_option('landlock').disabled() + if not get_option('seccomp') + warning('Seccomp sandboxing is disabled.') + endif + if get_option('landlock').disabled() + warning('Landlock sandboxing is disabled.') + endif + warning('Run at your own risk. Distribution is discouraged.') endif diff --git a/meson_options.txt b/meson_options.txt index 9bd367f50..c4e251bf8 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -16,7 +16,9 @@ option('miner_rss', type: 'boolean', value: true, option('writeback', type: 'boolean', value: true, description: 'Enable Tracker writeback feature') option('seccomp', type: 'boolean', value: true, - description: 'Enable seccomp support in Tracker metadata extractor') + description: 'Enable seccomp sandboxing support in Tracker metadata extractor') +option('landlock', type: 'feature', value: 'auto', + description: 'Enable landlock sandboxing support in Tracker metadata extractor') option('domain_prefix', type: 'string', value: 'org.freedesktop', description: 'Domain prefix to use, useful for sandboxing') option('miner_fs_cache_location', type: 'string', value: '', -- GitLab From cccc26f57a6a17debb2deea9e7e3b403b9753ba8 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sat, 2 Dec 2023 00:24:22 +0100 Subject: [PATCH 4/5] ci: Disable Landlock on non-seccomp test stage Landlock imposes similar restrictions to filesystem writes than seccomp, thus breaks coverage reports in the same way (since those need writing to files at runtime). Rename the "seccomp" stage/tasks to a more generic "sandbox" term, and disable both seccomp/landlock in the "test" stage so that coverage may work in tracker-extract. --- .gitlab-ci.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e0de1d4e3..2d5b68367 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -22,7 +22,7 @@ stages: - code-review - build - test - - seccomp + - sandbox - analysis - docs @@ -227,7 +227,7 @@ build-ubuntu-rolling@x86_64: - .gitlab-ci/checkout-tracker.sh - cd build - ninja clean - - meson configure -Db_coverage=true -Dseccomp=false + - meson configure -Db_coverage=true -Dseccomp=false -Dlandlock=disabled - ninja - | # Remove the many "CI_" variables from the environment. Meson dumps the @@ -313,8 +313,8 @@ test-docs: needs: - build-fedora-container@x86_64 -.seccomp_template: &seccomp - stage: seccomp +.sandbox_template: &sandbox + stage: sandbox script: - .gitlab-ci/checkout-tracker.sh @@ -334,39 +334,39 @@ test-docs: reports: junit: "build/meson-logs/testlog.junit.xml" -seccomp-fedora@x86_64: +sandbox-fedora@x86_64: extends: - .fdo.distribution-image@fedora - .tracker-miners.fedora:36@x86_64 needs: - build-fedora@x86_64 - <<: *seccomp + <<: *sandbox -seccomp-fedora-rawhide@x86_64: +sandbox-fedora-rawhide@x86_64: extends: - .fdo.distribution-image@fedora - .tracker-miners.fedora:rawhide@x86_64 needs: - build-fedora-rawhide@x86_64 allow_failure: true - <<: *seccomp + <<: *sandbox -seccomp-fedora@aarch64: +sandbox-fedora@aarch64: extends: - .fdo.distribution-image@fedora - .tracker-miners.fedora:36@aarch64 needs: - build-fedora@aarch64 allow_failure: true - <<: *seccomp + <<: *sandbox -seccomp-ubuntu@x86_64: +sandbox-ubuntu@x86_64: extends: - .fdo.distribution-image@ubuntu - .tracker-miners.ubuntu:rolling@x86_64 needs: - build-ubuntu-rolling@x86_64 - <<: *seccomp + <<: *sandbox coverage: extends: -- GitLab From 2feb828ab370d8d23cb9197c507efec3368a40b2 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Tue, 12 Dec 2023 10:54:11 +0100 Subject: [PATCH 5/5] libtracker-miners-common: Add "sandbox" TRACKER_DEBUG topic And log the Landlock rules with it. --- src/libtracker-miners-common/tracker-debug.c | 1 + src/libtracker-miners-common/tracker-debug.h | 1 + src/libtracker-miners-common/tracker-landlock.c | 5 +++++ 3 files changed, 7 insertions(+) diff --git a/src/libtracker-miners-common/tracker-debug.c b/src/libtracker-miners-common/tracker-debug.c index 0db8a369c..429eee0a3 100644 --- a/src/libtracker-miners-common/tracker-debug.c +++ b/src/libtracker-miners-common/tracker-debug.c @@ -29,6 +29,7 @@ static const GDebugKey tracker_miners_debug_keys[] = { { "monitors", TRACKER_DEBUG_MONITORS }, { "statistics", TRACKER_DEBUG_STATISTICS }, { "status", TRACKER_DEBUG_STATUS }, + { "sandbox", TRACKER_DEBUG_SANDBOX }, }; #endif /* G_ENABLE_DEBUG */ diff --git a/src/libtracker-miners-common/tracker-debug.h b/src/libtracker-miners-common/tracker-debug.h index 3c331dd57..605713d7a 100644 --- a/src/libtracker-miners-common/tracker-debug.h +++ b/src/libtracker-miners-common/tracker-debug.h @@ -35,6 +35,7 @@ typedef enum { TRACKER_DEBUG_MONITORS = 1 << 4, TRACKER_DEBUG_STATISTICS = 1 << 5, TRACKER_DEBUG_STATUS = 1 << 6, + TRACKER_DEBUG_SANDBOX = 1 << 7, } TrackerDebugFlag; #ifdef G_ENABLE_DEBUG diff --git a/src/libtracker-miners-common/tracker-landlock.c b/src/libtracker-miners-common/tracker-landlock.c index 3af08ef65..855dac161 100644 --- a/src/libtracker-miners-common/tracker-landlock.c +++ b/src/libtracker-miners-common/tracker-landlock.c @@ -29,6 +29,8 @@ #include #include +#include "tracker-debug.h" + /* Compensate for these syscalls not being wrapped in libc */ #define CREATE_RULESET(attr, flags) \ syscall (SYS_landlock_create_ruleset, (attr), \ @@ -107,6 +109,8 @@ add_rule (int landlock_fd, int fd; int result; + TRACKER_NOTE (SANDBOX, g_message ("Adding Landlock rule for '%s', flags %" G_GINT64_MODIFIER "x", path, flags)); + if (!g_file_test (path, G_FILE_TEST_EXISTS)) { g_debug ("Path %s does not exist in filesystem", path); return; @@ -298,6 +302,7 @@ tracker_landlock_init (const gchar * const *indexed_folders) LANDLOCK_ACCESS_FS_READ_FILE); #endif + TRACKER_NOTE (SANDBOX, g_message ("Applying Landlock ruleset to PID %d", getpid ())); retval = apply_ruleset (landlock_fd); close (landlock_fd); -- GitLab