summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
authorIago López Galeiras <iagol@microsoft.com>2021-02-11 16:59:30 +0100
committerIago Lopez Galeiras <iagol@microsoft.com>2021-10-06 10:52:14 +0200
commite59ccd035c94a8448d9b99bb0b8056ed3d3a339c (patch)
tree594049fe756c6a7fcaa6d439b1e81aa9f7369614 /src/core
parentcore: use LSM BPF functions to implement RestrictFileSystems= (diff)
downloadsystemd-e59ccd035c94a8448d9b99bb0b8056ed3d3a339c.tar.xz
systemd-e59ccd035c94a8448d9b99bb0b8056ed3d3a339c.zip
core: add RestrictFileSystems= fragment parser
It takes an allow or deny list of filesystems services should have access to.
Diffstat (limited to 'src/core')
-rw-r--r--src/core/bpf-lsm.c54
-rw-r--r--src/core/bpf-lsm.h12
-rw-r--r--src/core/load-fragment-gperf.gperf.in1
-rw-r--r--src/core/load-fragment.c72
-rw-r--r--src/core/load-fragment.h1
5 files changed, 140 insertions, 0 deletions
diff --git a/src/core/bpf-lsm.c b/src/core/bpf-lsm.c
index 3e480c6257..46c2446849 100644
--- a/src/core/bpf-lsm.c
+++ b/src/core/bpf-lsm.c
@@ -325,3 +325,57 @@ void lsm_bpf_destroy(struct restrict_fs_bpf *prog) {
return;
}
#endif
+
+int lsm_bpf_parse_filesystem(
+ const char *name,
+ Set **filesystems,
+ FilesystemParseFlags flags,
+ const char *unit,
+ const char *filename,
+ unsigned line) {
+ int r;
+
+ assert(name);
+ assert(filesystems);
+
+ if (name[0] == '@') {
+ const FilesystemSet *set;
+ const char *i;
+
+ set = filesystem_set_find(name);
+ if (!set) {
+ log_syntax(unit, flags & FILESYSTEM_PARSE_LOG ? LOG_WARNING : LOG_DEBUG, filename, line, 0,
+ "Unknown filesystem group, ignoring: %s", name);
+ return 0;
+ }
+
+ NULSTR_FOREACH(i, set->value) {
+ /* Call ourselves again, for the group to parse. Note that we downgrade logging here (i.e. take
+ * away the FILESYSTEM_PARSE_LOG flag) since any issues in the group table are our own problem,
+ * not a problem in user configuration data and we shouldn't pretend otherwise by complaining
+ * about them. */
+ r = lsm_bpf_parse_filesystem(i, filesystems, flags &~ FILESYSTEM_PARSE_LOG, unit, filename, line);
+ if (r < 0)
+ return r;
+ }
+ } else {
+ /* If we previously wanted to forbid access to a filesystem and now
+ * we want to allow it, then remove it from the list. */
+ if (!(flags & FILESYSTEM_PARSE_INVERT) == !!(flags & FILESYSTEM_PARSE_ALLOW_LIST)) {
+ r = set_put_strdup(filesystems, name);
+ if (r < 0)
+ switch (r) {
+ case -ENOMEM:
+ return flags & FILESYSTEM_PARSE_LOG ? log_oom() : -ENOMEM;
+ case -EEXIST:
+ /* Alredy in set, ignore */
+ break;
+ default:
+ return r;
+ }
+ } else
+ free(set_remove(*filesystems, name));
+ }
+
+ return 0;
+}
diff --git a/src/core/bpf-lsm.h b/src/core/bpf-lsm.h
index 625fb32b50..8bd58a29e5 100644
--- a/src/core/bpf-lsm.h
+++ b/src/core/bpf-lsm.h
@@ -3,6 +3,12 @@
#include "hashmap.h"
+typedef enum FilesystemParseFlags {
+ FILESYSTEM_PARSE_INVERT = 1 << 0,
+ FILESYSTEM_PARSE_ALLOW_LIST = 1 << 1,
+ FILESYSTEM_PARSE_LOG = 1 << 2,
+} FilesystemParseFlags;
+
typedef struct Unit Unit;
typedef struct Manager Manager;
@@ -14,3 +20,9 @@ int lsm_bpf_unit_restrict_filesystems(Unit *u, const Set *filesystems, bool allo
int lsm_bpf_cleanup(const Unit *u);
int lsm_bpf_map_restrict_fs_fd(Unit *u);
void lsm_bpf_destroy(struct restrict_fs_bpf *prog);
+int lsm_bpf_parse_filesystem(const char *name,
+ Set **filesystems,
+ FilesystemParseFlags flags,
+ const char *unit,
+ const char *filename,
+ unsigned line);
diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in
index 799a179430..480ec66bf9 100644
--- a/src/core/load-fragment-gperf.gperf.in
+++ b/src/core/load-fragment-gperf.gperf.in
@@ -81,6 +81,7 @@
{{type}}.RestrictAddressFamilies, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
{{type}}.LockPersonality, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
{% endif %}
+{{type}}.RestrictFileSystems, config_parse_restrict_filesystems, 0, offsetof({{type}}, exec_context)
{{type}}.LimitCPU, config_parse_rlimit, RLIMIT_CPU, offsetof({{type}}, exec_context.rlimit)
{{type}}.LimitFSIZE, config_parse_rlimit, RLIMIT_FSIZE, offsetof({{type}}, exec_context.rlimit)
{{type}}.LimitDATA, config_parse_rlimit, RLIMIT_DATA, offsetof({{type}}, exec_context.rlimit)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index f971084c28..10fe579aeb 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -19,6 +19,7 @@
#include "all-units.h"
#include "alloc-util.h"
#include "bpf-firewall.h"
+#include "bpf-lsm.h"
#include "bpf-program.h"
#include "bpf-socket-bind.h"
#include "bus-error.h"
@@ -3597,6 +3598,76 @@ int config_parse_restrict_namespaces(
}
#endif
+int config_parse_restrict_filesystems(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ ExecContext *c = data;
+ bool invert = false;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ c->restrict_filesystems = set_free(c->restrict_filesystems);
+ c->restrict_filesystems_allow_list = false;
+ return 0;
+ }
+
+ if (rvalue[0] == '~') {
+ invert = true;
+ rvalue++;
+ }
+
+ if (!c->restrict_filesystems) {
+ if (invert)
+ /* Allow everything but the ones listed */
+ c->restrict_filesystems_allow_list = false;
+ else
+ /* Allow nothing but the ones listed */
+ c->restrict_filesystems_allow_list = true;
+ }
+
+ for (const char *p = rvalue;;) {
+ _cleanup_free_ char *word = NULL;
+
+ r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
+ if (r == 0)
+ break;
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Trailing garbage in %s, ignoring: %s", lvalue, rvalue);
+ break;
+ }
+
+ r = lsm_bpf_parse_filesystem(
+ word,
+ &c->restrict_filesystems,
+ FILESYSTEM_PARSE_LOG|
+ (invert ? FILESYSTEM_PARSE_INVERT : 0)|
+ (c->restrict_filesystems_allow_list ? FILESYSTEM_PARSE_ALLOW_LIST : 0),
+ unit, filename, line);
+
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
int config_parse_unit_slice(
const char *unit,
const char *filename,
@@ -6030,6 +6101,7 @@ void unit_dump_config_items(FILE *f) {
{ config_parse_address_families, "FAMILIES" },
{ config_parse_restrict_namespaces, "NAMESPACES" },
#endif
+ { config_parse_restrict_filesystems, "FILESYSTEMS" },
{ config_parse_cpu_shares, "SHARES" },
{ config_parse_cg_weight, "WEIGHT" },
{ config_parse_memory_limit, "LIMIT" },
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index e84f9ee391..9173f30add 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -113,6 +113,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_sec_fix_0);
CONFIG_PARSER_PROTOTYPE(config_parse_user_group_compat);
CONFIG_PARSER_PROTOTYPE(config_parse_user_group_strv_compat);
CONFIG_PARSER_PROTOTYPE(config_parse_restrict_namespaces);
+CONFIG_PARSER_PROTOTYPE(config_parse_restrict_filesystems);
CONFIG_PARSER_PROTOTYPE(config_parse_bind_paths);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_keyring_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_protect_proc);