diff options
author | Iago López Galeiras <iagol@microsoft.com> | 2021-02-11 16:59:30 +0100 |
---|---|---|
committer | Iago Lopez Galeiras <iagol@microsoft.com> | 2021-10-06 10:52:14 +0200 |
commit | e59ccd035c94a8448d9b99bb0b8056ed3d3a339c (patch) | |
tree | 594049fe756c6a7fcaa6d439b1e81aa9f7369614 /src/core | |
parent | core: use LSM BPF functions to implement RestrictFileSystems= (diff) | |
download | systemd-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.c | 54 | ||||
-rw-r--r-- | src/core/bpf-lsm.h | 12 | ||||
-rw-r--r-- | src/core/load-fragment-gperf.gperf.in | 1 | ||||
-rw-r--r-- | src/core/load-fragment.c | 72 | ||||
-rw-r--r-- | src/core/load-fragment.h | 1 |
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); |