/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "af-list.h" #include "capability-util.h" #include "cgroup-setup.h" #include "escape.h" #include "exec-credential.h" #include "execute-serialize.h" #include "hexdecoct.h" #include "fd-util.h" #include "fileio.h" #include "in-addr-prefix-util.h" #include "parse-helpers.h" #include "parse-util.h" #include "percent-util.h" #include "process-util.h" #include "rlimit-util.h" #include "serialize.h" #include "string-util.h" #include "strv.h" static int exec_cgroup_context_serialize(const CGroupContext *c, FILE *f) { _cleanup_free_ char *disable_controllers_str = NULL, *delegate_controllers_str = NULL, *cpuset_cpus = NULL, *cpuset_mems = NULL, *startup_cpuset_cpus = NULL, *startup_cpuset_mems = NULL; char *iface; struct in_addr_prefix *iaai; int r; assert(f); if (!c) return 0; r = serialize_bool_elide(f, "exec-cgroup-context-cpu-accounting", c->cpu_accounting); if (r < 0) return r; r = serialize_bool_elide(f, "exec-cgroup-context-io-accounting", c->io_accounting); if (r < 0) return r; r = serialize_bool_elide(f, "exec-cgroup-context-block-io-accounting", c->blockio_accounting); if (r < 0) return r; r = serialize_bool_elide(f, "exec-cgroup-context-memory-accounting", c->memory_accounting); if (r < 0) return r; r = serialize_bool_elide(f, "exec-cgroup-context-tasks-accounting", c->tasks_accounting); if (r < 0) return r; r = serialize_bool_elide(f, "exec-cgroup-context-ip-accounting", c->ip_accounting); if (r < 0) return r; r = serialize_bool_elide(f, "exec-cgroup-context-memory-oom-group", c->memory_oom_group); if (r < 0) return r; if (c->cpu_weight != CGROUP_WEIGHT_INVALID) { r = serialize_item_format(f, "exec-cgroup-context-cpu-weight", "%" PRIu64, c->cpu_weight); if (r < 0) return r; } if (c->startup_cpu_weight != CGROUP_WEIGHT_INVALID) { r = serialize_item_format(f, "exec-cgroup-context-startup-cpu-weight", "%" PRIu64, c->startup_cpu_weight); if (r < 0) return r; } if (c->cpu_shares != CGROUP_CPU_SHARES_INVALID) { r = serialize_item_format(f, "exec-cgroup-context-cpu-shares", "%" PRIu64, c->cpu_shares); if (r < 0) return r; } if (c->startup_cpu_shares != CGROUP_CPU_SHARES_INVALID) { r = serialize_item_format(f, "exec-cgroup-context-startup-cpu-shares", "%" PRIu64, c->startup_cpu_shares); if (r < 0) return r; } if (c->cpu_quota_per_sec_usec != USEC_INFINITY) { r = serialize_usec(f, "exec-cgroup-context-cpu-quota-per-sec-usec", c->cpu_quota_per_sec_usec); if (r < 0) return r; } if (c->cpu_quota_period_usec != USEC_INFINITY) { r = serialize_usec(f, "exec-cgroup-context-cpu-quota-period-usec", c->cpu_quota_period_usec); if (r < 0) return r; } cpuset_cpus = cpu_set_to_range_string(&c->cpuset_cpus); if (!cpuset_cpus) return log_oom_debug(); r = serialize_item(f, "exec-cgroup-context-allowed-cpus", cpuset_cpus); if (r < 0) return r; startup_cpuset_cpus = cpu_set_to_range_string(&c->startup_cpuset_cpus); if (!startup_cpuset_cpus) return log_oom_debug(); r = serialize_item(f, "exec-cgroup-context-startup-allowed-cpus", startup_cpuset_cpus); if (r < 0) return r; cpuset_mems = cpu_set_to_range_string(&c->cpuset_mems); if (!cpuset_mems) return log_oom_debug(); r = serialize_item(f, "exec-cgroup-context-allowed-memory-nodes", cpuset_mems); if (r < 0) return r; startup_cpuset_mems = cpu_set_to_range_string(&c->startup_cpuset_mems); if (!startup_cpuset_mems) return log_oom_debug(); r = serialize_item(f, "exec-cgroup-context-startup-allowed-memory-nodes", startup_cpuset_mems); if (r < 0) return r; if (c->io_weight != CGROUP_WEIGHT_INVALID) { r = serialize_item_format(f, "exec-cgroup-context-io-weight", "%" PRIu64, c->io_weight); if (r < 0) return r; } if (c->startup_io_weight != CGROUP_WEIGHT_INVALID) { r = serialize_item_format(f, "exec-cgroup-context-startup-io-weight", "%" PRIu64, c->startup_io_weight); if (r < 0) return r; } if (c->blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID) { r = serialize_item_format(f, "exec-cgroup-context-block-io-weight", "%" PRIu64, c->blockio_weight); if (r < 0) return r; } if (c->startup_blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID) { r = serialize_item_format(f, "exec-cgroup-context-startup-block-io-weight", "%" PRIu64, c->startup_blockio_weight); if (r < 0) return r; } if (c->default_memory_min > 0) { r = serialize_item_format(f, "exec-cgroup-context-default-memory-min", "%" PRIu64, c->default_memory_min); if (r < 0) return r; } if (c->default_memory_low > 0) { r = serialize_item_format(f, "exec-cgroup-context-default-memory-low", "%" PRIu64, c->default_memory_low); if (r < 0) return r; } if (c->memory_min > 0) { r = serialize_item_format(f, "exec-cgroup-context-memory-min", "%" PRIu64, c->memory_min); if (r < 0) return r; } if (c->memory_low > 0) { r = serialize_item_format(f, "exec-cgroup-context-memory-low", "%" PRIu64, c->memory_low); if (r < 0) return r; } if (c->startup_memory_low > 0) { r = serialize_item_format(f, "exec-cgroup-context-startup-memory-low", "%" PRIu64, c->startup_memory_low); if (r < 0) return r; } if (c->memory_high != CGROUP_LIMIT_MAX) { r = serialize_item_format(f, "exec-cgroup-context-memory-high", "%" PRIu64, c->memory_high); if (r < 0) return r; } if (c->startup_memory_high != CGROUP_LIMIT_MAX) { r = serialize_item_format(f, "exec-cgroup-context-startup-memory-high", "%" PRIu64, c->startup_memory_high); if (r < 0) return r; } if (c->memory_max != CGROUP_LIMIT_MAX) { r = serialize_item_format(f, "exec-cgroup-context-memory-max", "%" PRIu64, c->memory_max); if (r < 0) return r; } if (c->startup_memory_max != CGROUP_LIMIT_MAX) { r = serialize_item_format(f, "exec-cgroup-context-startup-memory-max", "%" PRIu64, c->startup_memory_max); if (r < 0) return r; } if (c->memory_swap_max != CGROUP_LIMIT_MAX) { r = serialize_item_format(f, "exec-cgroup-context-memory-swap-max", "%" PRIu64, c->memory_swap_max); if (r < 0) return r; } if (c->startup_memory_swap_max != CGROUP_LIMIT_MAX) { r = serialize_item_format(f, "exec-cgroup-context-startup-memory-swap-max", "%" PRIu64, c->startup_memory_swap_max); if (r < 0) return r; } if (c->memory_zswap_max != CGROUP_LIMIT_MAX) { r = serialize_item_format(f, "exec-cgroup-context-memory-zswap-max", "%" PRIu64, c->memory_zswap_max); if (r < 0) return r; } if (c->startup_memory_zswap_max != CGROUP_LIMIT_MAX) { r = serialize_item_format(f, "exec-cgroup-context-startup-memory-zswap-max", "%" PRIu64, c->startup_memory_zswap_max); if (r < 0) return r; } r = serialize_bool(f, "exec-cgroup-context-memory-zswap-writeback", c->memory_zswap_writeback); if (r < 0) return r; if (c->memory_limit != CGROUP_LIMIT_MAX) { r = serialize_item_format(f, "exec-cgroup-context-memory-limit", "%" PRIu64, c->memory_limit); if (r < 0) return r; } if (c->tasks_max.value != UINT64_MAX) { r = serialize_item_format(f, "exec-cgroup-context-tasks-max-value", "%" PRIu64, c->tasks_max.value); if (r < 0) return r; } if (c->tasks_max.scale > 0) { r = serialize_item_format(f, "exec-cgroup-context-tasks-max-scale", "%" PRIu64, c->tasks_max.scale); if (r < 0) return r; } r = serialize_bool_elide(f, "exec-cgroup-context-default-memory-min-set", c->default_memory_min_set); if (r < 0) return r; r = serialize_bool_elide(f, "exec-cgroup-context-default-memory-low-set", c->default_memory_low_set); if (r < 0) return r; r = serialize_bool_elide(f, "exec-cgroup-context-default-startup-memory-low-set", c->default_startup_memory_low_set); if (r < 0) return r; r = serialize_bool_elide(f, "exec-cgroup-context-memory-min-set", c->memory_min_set); if (r < 0) return r; r = serialize_bool_elide(f, "exec-cgroup-context-memory-low-set", c->memory_low_set); if (r < 0) return r; r = serialize_bool_elide(f, "exec-cgroup-context-startup-memory-low-set", c->startup_memory_low_set); if (r < 0) return r; r = serialize_bool_elide(f, "exec-cgroup-context-startup-memory-high-set", c->startup_memory_high_set); if (r < 0) return r; r = serialize_bool_elide(f, "exec-cgroup-context-startup-memory-max-set", c->startup_memory_max_set); if (r < 0) return r; r = serialize_bool_elide(f, "exec-cgroup-context-startup-memory-swap-max-set", c->startup_memory_swap_max_set); if (r < 0) return r; r = serialize_bool_elide(f, "exec-cgroup-context-startup-memory-zswap-max-set", c->startup_memory_zswap_max_set); if (r < 0) return r; r = serialize_item(f, "exec-cgroup-context-device-policy", cgroup_device_policy_to_string(c->device_policy)); if (r < 0) return r; r = cg_mask_to_string(c->disable_controllers, &disable_controllers_str); if (r < 0) return r; r = serialize_item(f, "exec-cgroup-context-disable-controllers", disable_controllers_str); if (r < 0) return r; r = cg_mask_to_string(c->delegate_controllers, &delegate_controllers_str); if (r < 0) return r; r = serialize_item(f, "exec-cgroup-context-delegate-controllers", delegate_controllers_str); if (r < 0) return r; r = serialize_bool_elide(f, "exec-cgroup-context-delegate", c->delegate); if (r < 0) return r; r = serialize_item(f, "exec-cgroup-context-managed-oom-swap", managed_oom_mode_to_string(c->moom_swap)); if (r < 0) return r; r = serialize_item(f, "exec-cgroup-context-managed-oom-memory-pressure", managed_oom_mode_to_string(c->moom_mem_pressure)); if (r < 0) return r; r = serialize_item_format(f, "exec-cgroup-context-managed-oom-memory-pressure-limit", "%" PRIu32, c->moom_mem_pressure_limit); if (r < 0) return r; r = serialize_item(f, "exec-cgroup-context-managed-oom-preference", managed_oom_preference_to_string(c->moom_preference)); if (r < 0) return r; r = serialize_item(f, "exec-cgroup-context-memory-pressure-watch", cgroup_pressure_watch_to_string(c->memory_pressure_watch)); if (r < 0) return r; r = serialize_item(f, "exec-cgroup-context-delegate-subgroup", c->delegate_subgroup); if (r < 0) return r; if (c->memory_pressure_threshold_usec != USEC_INFINITY) { r = serialize_usec(f, "exec-cgroup-context-memory-pressure-threshold-usec", c->memory_pressure_threshold_usec); if (r < 0) return r; } LIST_FOREACH(device_allow, a, c->device_allow) { r = serialize_item_format(f, "exec-cgroup-context-device-allow", "%s %s", a->path, cgroup_device_permissions_to_string(a->permissions)); if (r < 0) return r; } LIST_FOREACH(device_weights, iw, c->io_device_weights) { r = serialize_item_format(f, "exec-cgroup-context-io-device-weight", "%s %" PRIu64, iw->path, iw->weight); if (r < 0) return r; } LIST_FOREACH(device_latencies, l, c->io_device_latencies) { r = serialize_item_format(f, "exec-cgroup-context-io-device-latency-target-usec", "%s " USEC_FMT, l->path, l->target_usec); if (r < 0) return r; } LIST_FOREACH(device_limits, il, c->io_device_limits) for (CGroupIOLimitType type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++) { _cleanup_free_ char *key = NULL; if (il->limits[type] == cgroup_io_limit_defaults[type]) continue; key = strjoin("exec-cgroup-context-io-device-limit-", cgroup_io_limit_type_to_string(type)); if (!key) return -ENOMEM; r = serialize_item_format(f, key, "%s %" PRIu64, il->path, il->limits[type]); if (r < 0) return r; } LIST_FOREACH(device_weights, w, c->blockio_device_weights) { r = serialize_item_format(f, "exec-cgroup-context-blockio-device-weight", "%s %" PRIu64, w->path, w->weight); if (r < 0) return r; } LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) { if (b->rbps != CGROUP_LIMIT_MAX) { r = serialize_item_format(f, "exec-cgroup-context-blockio-read-bandwidth", "%s %" PRIu64, b->path, b->rbps); if (r < 0) return r; } if (b->wbps != CGROUP_LIMIT_MAX) { r = serialize_item_format(f, "exec-cgroup-context-blockio-write-bandwidth", "%s %" PRIu64, b->path, b->wbps); if (r < 0) return r; } } SET_FOREACH(iaai, c->ip_address_allow) { r = serialize_item(f, "exec-cgroup-context-ip-address-allow", IN_ADDR_PREFIX_TO_STRING(iaai->family, &iaai->address, iaai->prefixlen)); if (r < 0) return r; } SET_FOREACH(iaai, c->ip_address_deny) { r = serialize_item(f, "exec-cgroup-context-ip-address-deny", IN_ADDR_PREFIX_TO_STRING(iaai->family, &iaai->address, iaai->prefixlen)); if (r < 0) return r; } r = serialize_bool_elide(f, "exec-cgroup-context-ip-address-allow-reduced", c->ip_address_allow_reduced); if (r < 0) return r; r = serialize_bool_elide(f, "exec-cgroup-context-ip-address-deny-reduced", c->ip_address_deny_reduced); if (r < 0) return r; r = serialize_strv(f, "exec-cgroup-context-ip-ingress-filter-path", c->ip_filters_ingress); if (r < 0) return r; r = serialize_strv(f, "exec-cgroup-context-ip-egress-filter-path", c->ip_filters_egress); if (r < 0) return r; LIST_FOREACH(programs, p, c->bpf_foreign_programs) { r = serialize_item_format(f, "exec-cgroup-context-bpf-program", "%" PRIu32 " %s", p->attach_type, p->bpffs_path); if (r < 0) return r; } LIST_FOREACH(socket_bind_items, bi, c->socket_bind_allow) { fprintf(f, "exec-cgroup-context-socket-bind-allow="); cgroup_context_dump_socket_bind_item(bi, f); fputc('\n', f); } LIST_FOREACH(socket_bind_items, bi, c->socket_bind_deny) { fprintf(f, "exec-cgroup-context-socket-bind-deny="); cgroup_context_dump_socket_bind_item(bi, f); fputc('\n', f); } SET_FOREACH(iface, c->restrict_network_interfaces) { r = serialize_item(f, "exec-cgroup-context-restrict-network-interfaces", iface); if (r < 0) return r; } r = serialize_bool_elide( f, "exec-cgroup-context-restrict-network-interfaces-is-allow-list", c->restrict_network_interfaces_is_allow_list); if (r < 0) return r; fputc('\n', f); /* End marker */ return 0; } static int exec_cgroup_context_deserialize(CGroupContext *c, FILE *f) { int r; assert(f); if (!c) return 0; for (;;) { _cleanup_free_ char *l = NULL; const char *val; r = deserialize_read_line(f, &l); if (r < 0) return r; if (r == 0) /* eof or end marker */ break; if ((val = startswith(l, "exec-cgroup-context-cpu-accounting="))) { r = parse_boolean(val); if (r < 0) return r; c->cpu_accounting = r; } else if ((val = startswith(l, "exec-cgroup-context-io-accounting="))) { r = parse_boolean(val); if (r < 0) return r; c->io_accounting = r; } else if ((val = startswith(l, "exec-cgroup-context-block-io-accounting="))) { r = parse_boolean(val); if (r < 0) return r; c->blockio_accounting = r; } else if ((val = startswith(l, "exec-cgroup-context-memory-accounting="))) { r = parse_boolean(val); if (r < 0) return r; c->memory_accounting = r; } else if ((val = startswith(l, "exec-cgroup-context-tasks-accounting="))) { r = parse_boolean(val); if (r < 0) return r; c->tasks_accounting = r; } else if ((val = startswith(l, "exec-cgroup-context-ip-accounting="))) { r = parse_boolean(val); if (r < 0) return r; c->ip_accounting = r; } else if ((val = startswith(l, "exec-cgroup-context-memory-oom-group="))) { r = parse_boolean(val); if (r < 0) return r; c->memory_oom_group = r; } else if ((val = startswith(l, "exec-cgroup-context-cpu-weight="))) { r = safe_atou64(val, &c->cpu_weight); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-startup-cpu-weight="))) { r = safe_atou64(val, &c->startup_cpu_weight); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-cpu-shares="))) { r = safe_atou64(val, &c->cpu_shares); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-startup-cpu-shares="))) { r = safe_atou64(val, &c->startup_cpu_shares); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-cpu-quota-per-sec-usec="))) { r = deserialize_usec(val, &c->cpu_quota_per_sec_usec); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-cpu-quota-period-usec="))) { r = deserialize_usec(val, &c->cpu_quota_period_usec); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-allowed-cpus="))) { if (c->cpuset_cpus.set) return -EINVAL; /* duplicated */ r = parse_cpu_set_full( val, &c->cpuset_cpus, /* warn= */ false, /* unit= */ NULL, /* filename= */ NULL, /* line= */ 0, /* lvalue= */ NULL); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-startup-allowed-cpus="))) { if (c->startup_cpuset_cpus.set) return -EINVAL; /* duplicated */ r = parse_cpu_set_full( val, &c->startup_cpuset_cpus, /* warn= */ false, /* unit= */ NULL, /* filename= */ NULL, /* line= */ 0, /* lvalue= */ NULL); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-allowed-memory-nodes="))) { if (c->cpuset_mems.set) return -EINVAL; /* duplicated */ r = parse_cpu_set_full( val, &c->cpuset_mems, /* warn= */ false, /* unit= */ NULL, /* filename= */ NULL, /* line= */ 0, /* lvalue= */ NULL); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-startup-allowed-memory-nodes="))) { if (c->startup_cpuset_mems.set) return -EINVAL; /* duplicated */ r = parse_cpu_set_full( val, &c->startup_cpuset_mems, /* warn= */ false, /* unit= */ NULL, /* filename= */ NULL, /* line= */ 0, /* lvalue= */ NULL); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-io-weight="))) { r = safe_atou64(val, &c->io_weight); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-startup-io-weight="))) { r = safe_atou64(val, &c->startup_io_weight); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-block-io-weight="))) { r = safe_atou64(val, &c->blockio_weight); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-startup-block-io-weight="))) { r = safe_atou64(val, &c->startup_blockio_weight); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-default-memory-min="))) { r = safe_atou64(val, &c->default_memory_min); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-default-memory-low="))) { r = safe_atou64(val, &c->default_memory_low); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-memory-min="))) { r = safe_atou64(val, &c->memory_min); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-memory-low="))) { r = safe_atou64(val, &c->memory_low); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-startup-memory-low="))) { r = safe_atou64(val, &c->startup_memory_low); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-memory-high="))) { r = safe_atou64(val, &c->memory_high); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-startup-memory-high="))) { r = safe_atou64(val, &c->startup_memory_high); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-memory-max="))) { r = safe_atou64(val, &c->memory_max); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-startup-memory-max="))) { r = safe_atou64(val, &c->startup_memory_max); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-memory-swap-max="))) { r = safe_atou64(val, &c->memory_swap_max); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-startup-memory-swap-max="))) { r = safe_atou64(val, &c->startup_memory_swap_max); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-memory-zswap-max="))) { r = safe_atou64(val, &c->memory_zswap_max); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-startup-memory-zswap-max="))) { r = safe_atou64(val, &c->startup_memory_zswap_max); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-memory-zswap-writeback="))) { r = parse_boolean(val); if (r < 0) return r; c->memory_zswap_writeback = r; } else if ((val = startswith(l, "exec-cgroup-context-memory-limit="))) { r = safe_atou64(val, &c->memory_limit); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-tasks-max-value="))) { r = safe_atou64(val, &c->tasks_max.value); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-tasks-max-scale="))) { r = safe_atou64(val, &c->tasks_max.scale); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-default-memory-min-set="))) { r = parse_boolean(val); if (r < 0) return r; c->default_memory_min_set = r; } else if ((val = startswith(l, "exec-cgroup-context-default-memory-low-set="))) { r = parse_boolean(val); if (r < 0) return r; c->default_memory_low_set = r; } else if ((val = startswith(l, "exec-cgroup-context-default-startup-memory-low-set="))) { r = parse_boolean(val); if (r < 0) return r; c->default_startup_memory_low_set = r; } else if ((val = startswith(l, "exec-cgroup-context-memory-min-set="))) { r = parse_boolean(val); if (r < 0) return r; c->memory_min_set = r; } else if ((val = startswith(l, "exec-cgroup-context-memory-low-set="))) { r = parse_boolean(val); if (r < 0) return r; c->memory_low_set = r; } else if ((val = startswith(l, "exec-cgroup-context-startup-memory-low-set="))) { r = parse_boolean(val); if (r < 0) return r; c->startup_memory_low_set = r; } else if ((val = startswith(l, "exec-cgroup-context-startup-memory-high-set="))) { r = parse_boolean(val); if (r < 0) return r; c->startup_memory_high_set = r; } else if ((val = startswith(l, "exec-cgroup-context-startup-memory-max-set="))) { r = parse_boolean(val); if (r < 0) return r; c->startup_memory_max_set = r; } else if ((val = startswith(l, "exec-cgroup-context-startup-memory-swap-max-set="))) { r = parse_boolean(val); if (r < 0) return r; c->startup_memory_swap_max_set = r; } else if ((val = startswith(l, "exec-cgroup-context-startup-memory-zswap-max-set="))) { r = parse_boolean(val); if (r < 0) return r; c->startup_memory_zswap_max_set = r; } else if ((val = startswith(l, "exec-cgroup-context-device-policy="))) { c->device_policy = cgroup_device_policy_from_string(val); if (c->device_policy < 0) return -EINVAL; } else if ((val = startswith(l, "exec-cgroup-context-disable-controllers="))) { r = cg_mask_from_string(val, &c->disable_controllers); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-delegate-controllers="))) { r = cg_mask_from_string(val, &c->delegate_controllers); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-delegate="))) { r = parse_boolean(val); if (r < 0) return r; c->delegate = r; } else if ((val = startswith(l, "exec-cgroup-context-managed-oom-swap="))) { c->moom_swap = managed_oom_mode_from_string(val); if (c->moom_swap < 0) return -EINVAL; } else if ((val = startswith(l, "exec-cgroup-context-managed-oom-memory-pressure="))) { c->moom_mem_pressure = managed_oom_mode_from_string(val); if (c->moom_mem_pressure < 0) return -EINVAL; } else if ((val = startswith(l, "exec-cgroup-context-managed-oom-memory-pressure-limit="))) { r = safe_atou32(val, &c->moom_mem_pressure_limit); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-managed-oom-preference="))) { c->moom_preference = managed_oom_preference_from_string(val); if (c->moom_preference < 0) return -EINVAL; } else if ((val = startswith(l, "exec-cgroup-context-memory-pressure-watch="))) { c->memory_pressure_watch = cgroup_pressure_watch_from_string(val); if (c->memory_pressure_watch < 0) return -EINVAL; } else if ((val = startswith(l, "exec-cgroup-context-delegate-subgroup="))) { r = free_and_strdup(&c->delegate_subgroup, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-memory-pressure-threshold-usec="))) { r = deserialize_usec(val, &c->memory_pressure_threshold_usec); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-device-allow="))) { _cleanup_free_ char *path = NULL, *rwm = NULL; CGroupDevicePermissions p; r = extract_many_words(&val, " ", 0, &path, &rwm); if (r < 0) return r; if (r == 0) return -EINVAL; p = isempty(rwm) ? 0 : cgroup_device_permissions_from_string(rwm); if (p < 0) return p; r = cgroup_context_add_or_update_device_allow(c, path, p); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-io-device-weight="))) { _cleanup_free_ char *path = NULL, *weight = NULL; CGroupIODeviceWeight *a = NULL; r = extract_many_words(&val, " ", 0, &path, &weight); if (r < 0) return r; if (r != 2) return -EINVAL; LIST_FOREACH(device_weights, b, c->io_device_weights) if (path_equal(b->path, path)) { a = b; break; } if (!a) { a = new0(CGroupIODeviceWeight, 1); if (!a) return log_oom_debug(); a->path = TAKE_PTR(path); LIST_PREPEND(device_weights, c->io_device_weights, a); } r = safe_atou64(weight, &a->weight); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-io-device-latency-target-usec="))) { _cleanup_free_ char *path = NULL, *target = NULL; CGroupIODeviceLatency *a = NULL; r = extract_many_words(&val, " ", 0, &path, &target); if (r < 0) return r; if (r != 2) return -EINVAL; LIST_FOREACH(device_latencies, b, c->io_device_latencies) if (path_equal(b->path, path)) { a = b; break; } if (!a) { a = new0(CGroupIODeviceLatency, 1); if (!a) return log_oom_debug(); a->path = TAKE_PTR(path); LIST_PREPEND(device_latencies, c->io_device_latencies, a); } r = deserialize_usec(target, &a->target_usec); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-io-device-limit-"))) { _cleanup_free_ char *type = NULL, *path = NULL, *limits = NULL; CGroupIODeviceLimit *limit = NULL; CGroupIOLimitType t; r = extract_many_words(&val, "= ", 0, &type, &path, &limits); if (r < 0) return r; if (r != 3) return -EINVAL; t = cgroup_io_limit_type_from_string(type); if (t < 0) return t; LIST_FOREACH(device_limits, i, c->io_device_limits) if (path_equal(path, i->path)) { limit = i; break; } if (!limit) { limit = new0(CGroupIODeviceLimit, 1); if (!limit) return log_oom_debug(); limit->path = TAKE_PTR(path); for (CGroupIOLimitType i = 0; i < _CGROUP_IO_LIMIT_TYPE_MAX; i++) limit->limits[i] = cgroup_io_limit_defaults[i]; LIST_PREPEND(device_limits, c->io_device_limits, limit); } r = safe_atou64(limits, &limit->limits[t]); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-block-io-device-weight="))) { _cleanup_free_ char *path = NULL, *weight = NULL; CGroupBlockIODeviceWeight *a = NULL; r = extract_many_words(&val, " ", 0, &path, &weight); if (r < 0) return r; if (r != 2) return -EINVAL; a = new0(CGroupBlockIODeviceWeight, 1); if (!a) return log_oom_debug(); a->path = TAKE_PTR(path); LIST_PREPEND(device_weights, c->blockio_device_weights, a); r = safe_atou64(weight, &a->weight); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-block-io-read-bandwidth="))) { _cleanup_free_ char *path = NULL, *bw = NULL; CGroupBlockIODeviceBandwidth *a = NULL; r = extract_many_words(&val, " ", 0, &path, &bw); if (r < 0) return r; if (r != 2) return -EINVAL; LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) if (path_equal(b->path, path)) { a = b; break; } if (!a) { a = new0(CGroupBlockIODeviceBandwidth, 1); if (!a) return log_oom_debug(); a->path = TAKE_PTR(path); a->wbps = CGROUP_LIMIT_MAX; LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, a); } r = safe_atou64(bw, &a->rbps); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-block-io-write-bandwidth="))) { _cleanup_free_ char *path = NULL, *bw = NULL; CGroupBlockIODeviceBandwidth *a = NULL; r = extract_many_words(&val, " ", 0, &path, &bw); if (r < 0) return r; if (r != 2) return -EINVAL; LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) if (path_equal(b->path, path)) { a = b; break; } if (!a) { a = new0(CGroupBlockIODeviceBandwidth, 1); if (!a) return log_oom_debug(); a->path = TAKE_PTR(path); a->rbps = CGROUP_LIMIT_MAX; LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, a); } r = safe_atou64(bw, &a->wbps); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-ip-address-allow="))) { struct in_addr_prefix a; r = in_addr_prefix_from_string_auto(val, &a.family, &a.address, &a.prefixlen); if (r < 0) return r; r = in_addr_prefix_add(&c->ip_address_allow, &a); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-ip-address-deny="))) { struct in_addr_prefix a; r = in_addr_prefix_from_string_auto(val, &a.family, &a.address, &a.prefixlen); if (r < 0) return r; r = in_addr_prefix_add(&c->ip_address_deny, &a); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-ip-address-allow-reduced="))) { r = parse_boolean(val); if (r < 0) return r; c->ip_address_allow_reduced = r; } else if ((val = startswith(l, "exec-cgroup-context-ip-address-deny-reduced="))) { r = parse_boolean(val); if (r < 0) return r; c->ip_address_deny_reduced = r; } else if ((val = startswith(l, "exec-cgroup-context-ip-ingress-filter-path="))) { r = deserialize_strv(val, &c->ip_filters_ingress); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-ip-egress-filter-path="))) { r = deserialize_strv(val, &c->ip_filters_egress); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-bpf-program="))) { _cleanup_free_ char *type = NULL, *path = NULL; uint32_t t; r = extract_many_words(&val, " ", 0, &type, &path); if (r < 0) return r; if (r != 2) return -EINVAL; r = safe_atou32(type, &t); if (r < 0) return r; r = cgroup_context_add_bpf_foreign_program(c, t, path); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-socket-bind-allow="))) { CGroupSocketBindItem *item; uint16_t nr_ports, port_min; int af, ip_protocol; r = parse_socket_bind_item(val, &af, &ip_protocol, &nr_ports, &port_min); if (r < 0) return r; item = new(CGroupSocketBindItem, 1); if (!item) return log_oom_debug(); *item = (CGroupSocketBindItem) { .address_family = af, .ip_protocol = ip_protocol, .nr_ports = nr_ports, .port_min = port_min, }; LIST_PREPEND(socket_bind_items, c->socket_bind_allow, item); } else if ((val = startswith(l, "exec-cgroup-context-socket-bind-deny="))) { CGroupSocketBindItem *item; uint16_t nr_ports, port_min; int af, ip_protocol; r = parse_socket_bind_item(val, &af, &ip_protocol, &nr_ports, &port_min); if (r < 0) return r; item = new(CGroupSocketBindItem, 1); if (!item) return log_oom_debug(); *item = (CGroupSocketBindItem) { .address_family = af, .ip_protocol = ip_protocol, .nr_ports = nr_ports, .port_min = port_min, }; LIST_PREPEND(socket_bind_items, c->socket_bind_deny, item); } else if ((val = startswith(l, "exec-cgroup-context-restrict-network-interfaces="))) { r = set_ensure_allocated(&c->restrict_network_interfaces, &string_hash_ops); if (r < 0) return r; r = set_put_strdup(&c->restrict_network_interfaces, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-cgroup-context-restrict-network-interfaces-is-allow-list="))) { r = parse_boolean(val); if (r < 0) return r; c->restrict_network_interfaces_is_allow_list = r; } else log_warning("Failed to parse serialized line, ignoring: %s", l); } return 0; } static int exec_runtime_serialize(const ExecRuntime *rt, FILE *f, FDSet *fds) { int r; assert(f); assert(fds); if (!rt) { fputc('\n', f); /* End marker */ return 0; } if (rt->shared) { r = serialize_item(f, "exec-runtime-id", rt->shared->id); if (r < 0) return r; r = serialize_item(f, "exec-runtime-tmp-dir", rt->shared->tmp_dir); if (r < 0) return r; r = serialize_item(f, "exec-runtime-var-tmp-dir", rt->shared->var_tmp_dir); if (r < 0) return r; if (rt->shared->netns_storage_socket[0] >= 0 && rt->shared->netns_storage_socket[1] >= 0) { r = serialize_fd_many(f, fds, "exec-runtime-netns-storage-socket", rt->shared->netns_storage_socket, 2); if (r < 0) return r; } if (rt->shared->ipcns_storage_socket[0] >= 0 && rt->shared->ipcns_storage_socket[1] >= 0) { r = serialize_fd_many(f, fds, "exec-runtime-ipcns-storage-socket", rt->shared->ipcns_storage_socket, 2); if (r < 0) return r; } } if (rt->dynamic_creds) { r = dynamic_user_serialize_one(rt->dynamic_creds->user, "exec-runtime-dynamic-creds-user", f, fds); if (r < 0) return r; } if (rt->dynamic_creds && rt->dynamic_creds->group && rt->dynamic_creds->group == rt->dynamic_creds->user) { r = serialize_bool(f, "exec-runtime-dynamic-creds-group-copy", true); if (r < 0) return r; } else if (rt->dynamic_creds) { r = dynamic_user_serialize_one(rt->dynamic_creds->group, "exec-runtime-dynamic-creds-group", f, fds); if (r < 0) return r; } r = serialize_item(f, "exec-runtime-ephemeral-copy", rt->ephemeral_copy); if (r < 0) return r; if (rt->ephemeral_storage_socket[0] >= 0 && rt->ephemeral_storage_socket[1] >= 0) { r = serialize_fd_many(f, fds, "exec-runtime-ephemeral-storage-socket", rt->ephemeral_storage_socket, 2); if (r < 0) return r; } fputc('\n', f); /* End marker */ return 0; } static int exec_runtime_deserialize(ExecRuntime *rt, FILE *f, FDSet *fds) { int r; assert(rt); assert(rt->shared); assert(rt->dynamic_creds); assert(f); assert(fds); for (;;) { _cleanup_free_ char *l = NULL; const char *val; r = deserialize_read_line(f, &l); if (r < 0) return r; if (r == 0) /* eof or end marker */ break; if ((val = startswith(l, "exec-runtime-id="))) { r = free_and_strdup(&rt->shared->id, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-runtime-tmp-dir="))) { r = free_and_strdup(&rt->shared->tmp_dir, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-runtime-var-tmp-dir="))) { r = free_and_strdup(&rt->shared->var_tmp_dir, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-runtime-netns-storage-socket="))) { r = deserialize_fd_many(fds, val, 2, rt->shared->netns_storage_socket); if (r < 0) continue; } else if ((val = startswith(l, "exec-runtime-ipcns-storage-socket="))) { r = deserialize_fd_many(fds, val, 2, rt->shared->ipcns_storage_socket); if (r < 0) continue; } else if ((val = startswith(l, "exec-runtime-dynamic-creds-user="))) dynamic_user_deserialize_one(/* m= */ NULL, val, fds, &rt->dynamic_creds->user); else if ((val = startswith(l, "exec-runtime-dynamic-creds-group="))) dynamic_user_deserialize_one(/* m= */ NULL, val, fds, &rt->dynamic_creds->group); else if ((val = startswith(l, "exec-runtime-dynamic-creds-group-copy="))) { r = parse_boolean(val); if (r < 0) return r; if (!r) continue; /* Nothing to do */ if (!rt->dynamic_creds->user) return -EINVAL; rt->dynamic_creds->group = dynamic_user_ref(rt->dynamic_creds->user); } else if ((val = startswith(l, "exec-runtime-ephemeral-copy="))) { r = free_and_strdup(&rt->ephemeral_copy, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-runtime-ephemeral-storage-socket="))) { r = deserialize_fd_many(fds, val, 2, rt->ephemeral_storage_socket); if (r < 0) continue; } else log_warning("Failed to parse serialized line, ignoring: %s", l); } return 0; } static bool exec_parameters_is_idle_pipe_set(const ExecParameters *p) { assert(p); return p->idle_pipe && p->idle_pipe[0] >= 0 && p->idle_pipe[1] >= 0 && p->idle_pipe[2] >= 0 && p->idle_pipe[3] >= 0; } static int exec_parameters_serialize(const ExecParameters *p, const ExecContext *c, FILE *f, FDSet *fds) { int r; assert(f); assert(fds); if (!p) return 0; r = serialize_item(f, "exec-parameters-runtime-scope", runtime_scope_to_string(p->runtime_scope)); if (r < 0) return r; r = serialize_strv(f, "exec-parameters-environment", p->environment); if (r < 0) return r; if (p->fds) { if (p->n_socket_fds > 0) { r = serialize_item_format(f, "exec-parameters-n-socket-fds", "%zu", p->n_socket_fds); if (r < 0) return r; } if (p->n_storage_fds > 0) { r = serialize_item_format(f, "exec-parameters-n-storage-fds", "%zu", p->n_storage_fds); if (r < 0) return r; } r = serialize_fd_many(f, fds, "exec-parameters-fds", p->fds, p->n_socket_fds + p->n_storage_fds); if (r < 0) return r; } r = serialize_strv(f, "exec-parameters-fd-names", p->fd_names); if (r < 0) return r; if (p->flags != 0) { r = serialize_item_format(f, "exec-parameters-flags", "%u", (unsigned) p->flags); if (r < 0) return r; } r = serialize_bool_elide(f, "exec-parameters-selinux-context-net", p->selinux_context_net); if (r < 0) return r; if (p->cgroup_supported != 0) { r = serialize_item_format(f, "exec-parameters-cgroup-supported", "%u", (unsigned) p->cgroup_supported); if (r < 0) return r; } r = serialize_item(f, "exec-parameters-cgroup-path", p->cgroup_path); if (r < 0) return r; r = serialize_item_format(f, "exec-parameters-cgroup-id", "%" PRIu64, p->cgroup_id); if (r < 0) return r; for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) { _cleanup_free_ char *key = NULL; key = strjoin("exec-parameters-prefix-directories-", exec_directory_type_to_string(dt)); if (!key) return log_oom_debug(); /* Always serialize, even an empty prefix, as this is a fixed array and we always expect * to have all elements (unless fuzzing is happening, hence the NULL check). */ r = serialize_item(f, key, strempty(p->prefix ? p->prefix[dt] : NULL)); if (r < 0) return r; } r = serialize_item(f, "exec-parameters-received-credentials-directory", p->received_credentials_directory); if (r < 0) return r; r = serialize_item(f, "exec-parameters-received-encrypted-credentials-directory", p->received_encrypted_credentials_directory); if (r < 0) return r; r = serialize_item(f, "exec-parameters-confirm-spawn", p->confirm_spawn); if (r < 0) return r; r = serialize_bool_elide(f, "exec-parameters-shall-confirm-spawn", p->shall_confirm_spawn); if (r < 0) return r; if (p->watchdog_usec > 0) { r = serialize_usec(f, "exec-parameters-watchdog-usec", p->watchdog_usec); if (r < 0) return r; } if (exec_parameters_is_idle_pipe_set(p)) { r = serialize_fd_many(f, fds, "exec-parameters-idle-pipe", p->idle_pipe, 4); if (r < 0) return r; } r = serialize_fd(f, fds, "exec-parameters-stdin-fd", p->stdin_fd); if (r < 0) return r; r = serialize_fd(f, fds, "exec-parameters-stdout-fd", p->stdout_fd); if (r < 0) return r; r = serialize_fd(f, fds, "exec-parameters-stderr-fd", p->stderr_fd); if (r < 0) return r; r = serialize_fd(f, fds, "exec-parameters-exec-fd", p->exec_fd); if (r < 0) return r; r = serialize_fd(f, fds, "exec-parameters-handoff-timestamp-fd", p->handoff_timestamp_fd); if (r < 0) return r; if (c && exec_context_restrict_filesystems_set(c)) { r = serialize_fd(f, fds, "exec-parameters-bpf-outer-map-fd", p->bpf_restrict_fs_map_fd); if (r < 0) return r; } r = serialize_item(f, "exec-parameters-notify-socket", p->notify_socket); if (r < 0) return r; LIST_FOREACH(open_files, file, p->open_files) { _cleanup_free_ char *ofs = NULL; r = open_file_to_string(file, &ofs); if (r < 0) return r; r = serialize_item(f, "exec-parameters-open-file", ofs); if (r < 0) return r; } r = serialize_item(f, "exec-parameters-fallback-smack-process-label", p->fallback_smack_process_label); if (r < 0) return r; r = serialize_fd(f, fds, "exec-parameters-user-lookup-fd", p->user_lookup_fd); if (r < 0) return r; r = serialize_strv(f, "exec-parameters-files-env", p->files_env); if (r < 0) return r; r = serialize_item(f, "exec-parameters-unit-id", p->unit_id); if (r < 0) return r; r = serialize_item(f, "exec-parameters-invocation-id-string", p->invocation_id_string); if (r < 0) return r; fputc('\n', f); /* End marker */ return 0; } static int exec_parameters_deserialize(ExecParameters *p, FILE *f, FDSet *fds) { int r, nr_open; assert(p); assert(f); assert(fds); nr_open = read_nr_open(); if (nr_open < 3) nr_open = HIGH_RLIMIT_NOFILE; assert(nr_open > 0); /* For compilers/static analyzers */ for (;;) { _cleanup_free_ char *l = NULL; const char *val; r = deserialize_read_line(f, &l); if (r < 0) return r; if (r == 0) /* eof or end marker */ break; if ((val = startswith(l, "exec-parameters-runtime-scope="))) { p->runtime_scope = runtime_scope_from_string(val); if (p->runtime_scope < 0) return p->runtime_scope; } else if ((val = startswith(l, "exec-parameters-environment="))) { r = deserialize_strv(val, &p->environment); if (r < 0) return r; } else if ((val = startswith(l, "exec-parameters-n-socket-fds="))) { if (p->fds) return -EINVAL; /* Already received */ r = safe_atozu(val, &p->n_socket_fds); if (r < 0) return r; if (p->n_socket_fds > (size_t) nr_open) return -EINVAL; /* too many, someone is playing games with us */ } else if ((val = startswith(l, "exec-parameters-n-storage-fds="))) { if (p->fds) return -EINVAL; /* Already received */ r = safe_atozu(val, &p->n_storage_fds); if (r < 0) return r; if (p->n_storage_fds > (size_t) nr_open) return -EINVAL; /* too many, someone is playing games with us */ } else if ((val = startswith(l, "exec-parameters-fds="))) { if (p->n_socket_fds + p->n_storage_fds == 0) return log_warning_errno( SYNTHETIC_ERRNO(EINVAL), "Got exec-parameters-fds= without " "prior exec-parameters-n-socket-fds= or exec-parameters-n-storage-fds="); if (p->n_socket_fds + p->n_storage_fds > (size_t) nr_open) return -EINVAL; /* too many, someone is playing games with us */ if (p->fds) return -EINVAL; /* duplicated */ p->fds = new(int, p->n_socket_fds + p->n_storage_fds); if (!p->fds) return log_oom_debug(); /* Ensure we don't leave any FD uninitialized on error, it makes the fuzzer sad */ FOREACH_ARRAY(i, p->fds, p->n_socket_fds + p->n_storage_fds) *i = -EBADF; r = deserialize_fd_many(fds, val, p->n_socket_fds + p->n_storage_fds, p->fds); if (r < 0) continue; } else if ((val = startswith(l, "exec-parameters-fd-names="))) { r = deserialize_strv(val, &p->fd_names); if (r < 0) return r; } else if ((val = startswith(l, "exec-parameters-flags="))) { unsigned flags; r = safe_atou(val, &flags); if (r < 0) return r; p->flags = flags; } else if ((val = startswith(l, "exec-parameters-selinux-context-net="))) { r = parse_boolean(val); if (r < 0) return r; p->selinux_context_net = r; } else if ((val = startswith(l, "exec-parameters-cgroup-supported="))) { unsigned cgroup_supported; r = safe_atou(val, &cgroup_supported); if (r < 0) return r; p->cgroup_supported = cgroup_supported; } else if ((val = startswith(l, "exec-parameters-cgroup-path="))) { r = free_and_strdup(&p->cgroup_path, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-parameters-cgroup-id="))) { r = safe_atou64(val, &p->cgroup_id); if (r < 0) return r; } else if ((val = startswith(l, "exec-parameters-prefix-directories-"))) { _cleanup_free_ char *type = NULL, *prefix = NULL; ExecDirectoryType dt; r = extract_many_words(&val, "= ", 0, &type, &prefix); if (r < 0) return r; if (r == 0) return -EINVAL; dt = exec_directory_type_from_string(type); if (dt < 0) return -EINVAL; if (!p->prefix) { p->prefix = new0(char*, _EXEC_DIRECTORY_TYPE_MAX+1); if (!p->prefix) return log_oom_debug(); } if (isempty(prefix)) p->prefix[dt] = mfree(p->prefix[dt]); else free_and_replace(p->prefix[dt], prefix); } else if ((val = startswith(l, "exec-parameters-received-credentials-directory="))) { r = free_and_strdup(&p->received_credentials_directory, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-parameters-received-encrypted-credentials-directory="))) { r = free_and_strdup(&p->received_encrypted_credentials_directory, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-parameters-confirm-spawn="))) { r = free_and_strdup(&p->confirm_spawn, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-parameters-shall-confirm-spawn="))) { r = parse_boolean(val); if (r < 0) return r; p->shall_confirm_spawn = r; } else if ((val = startswith(l, "exec-parameters-watchdog-usec="))) { r = deserialize_usec(val, &p->watchdog_usec); if (r < 0) return r; } else if ((val = startswith(l, "exec-parameters-idle-pipe="))) { if (p->idle_pipe) return -EINVAL; /* duplicated */ p->idle_pipe = new(int, 4); if (!p->idle_pipe) return log_oom_debug(); p->idle_pipe[0] = p->idle_pipe[1] = p->idle_pipe[2] = p->idle_pipe[3] = -EBADF; r = deserialize_fd_many(fds, val, 4, p->idle_pipe); if (r < 0) continue; } else if ((val = startswith(l, "exec-parameters-stdin-fd="))) { int fd; fd = deserialize_fd(fds, val); if (fd < 0) continue; close_and_replace(p->stdin_fd, fd); } else if ((val = startswith(l, "exec-parameters-stdout-fd="))) { int fd; fd = deserialize_fd(fds, val); if (fd < 0) continue; close_and_replace(p->stdout_fd, fd); } else if ((val = startswith(l, "exec-parameters-stderr-fd="))) { int fd; fd = deserialize_fd(fds, val); if (fd < 0) continue; close_and_replace(p->stderr_fd, fd); } else if ((val = startswith(l, "exec-parameters-exec-fd="))) { int fd; fd = deserialize_fd(fds, val); if (fd < 0) continue; close_and_replace(p->exec_fd, fd); } else if ((val = startswith(l, "exec-parameters-handoff-timestamp-fd="))) { int fd; fd = deserialize_fd(fds, val); if (fd < 0) continue; close_and_replace(p->handoff_timestamp_fd, fd); } else if ((val = startswith(l, "exec-parameters-bpf-outer-map-fd="))) { int fd; fd = deserialize_fd(fds, val); if (fd < 0) continue; close_and_replace(p->bpf_restrict_fs_map_fd, fd); } else if ((val = startswith(l, "exec-parameters-notify-socket="))) { r = free_and_strdup(&p->notify_socket, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-parameters-open-file="))) { OpenFile *of; r = open_file_parse(val, &of); if (r < 0) return r; LIST_APPEND(open_files, p->open_files, of); } else if ((val = startswith(l, "exec-parameters-fallback-smack-process-label="))) { r = free_and_strdup(&p->fallback_smack_process_label, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-parameters-user-lookup-fd="))) { int fd; fd = deserialize_fd(fds, val); if (fd < 0) continue; close_and_replace(p->user_lookup_fd, fd); } else if ((val = startswith(l, "exec-parameters-files-env="))) { r = deserialize_strv(val, &p->files_env); if (r < 0) return r; } else if ((val = startswith(l, "exec-parameters-unit-id="))) { r = free_and_strdup(&p->unit_id, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-parameters-invocation-id-string="))) { if (strlen(val) > SD_ID128_STRING_MAX - 1) return -EINVAL; r = sd_id128_from_string(val, &p->invocation_id); if (r < 0) return r; sd_id128_to_string(p->invocation_id, p->invocation_id_string); } else log_warning("Failed to parse serialized line, ignoring: %s", l); } /* Bail out if we got exec-parameters-n-{socket/storage}-fds= but no corresponding * exec-parameters-fds= */ if (p->n_socket_fds + p->n_storage_fds > 0 && !p->fds) return -EINVAL; return 0; } static int serialize_std_out_err(const ExecContext *c, FILE *f, int fileno) { char *key, *value; const char *type; assert(c); assert(f); assert(IN_SET(fileno, STDOUT_FILENO, STDERR_FILENO)); type = fileno == STDOUT_FILENO ? "output" : "error"; switch (fileno == STDOUT_FILENO ? c->std_output : c->std_error) { case EXEC_OUTPUT_NAMED_FD: key = strjoina("exec-context-std-", type, "-fd-name"); value = c->stdio_fdname[fileno]; break; case EXEC_OUTPUT_FILE: key = strjoina("exec-context-std-", type, "-file"); value = c->stdio_file[fileno]; break; case EXEC_OUTPUT_FILE_APPEND: key = strjoina("exec-context-std-", type, "-file-append"); value = c->stdio_file[fileno]; break; case EXEC_OUTPUT_FILE_TRUNCATE: key = strjoina("exec-context-std-", type, "-file-truncate"); value = c->stdio_file[fileno]; break; default: return 0; } return serialize_item(f, key, value); } static int exec_context_serialize(const ExecContext *c, FILE *f) { int r; assert(f); if (!c) return 0; r = serialize_strv(f, "exec-context-environment", c->environment); if (r < 0) return r; r = serialize_strv(f, "exec-context-environment-files", c->environment_files); if (r < 0) return r; r = serialize_strv(f, "exec-context-pass-environment", c->pass_environment); if (r < 0) return r; r = serialize_strv(f, "exec-context-unset-environment", c->unset_environment); if (r < 0) return r; r = serialize_item_escaped(f, "exec-context-working-directory", c->working_directory); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-working-directory-missing-ok", c->working_directory_missing_ok); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-working-directory-home", c->working_directory_home); if (r < 0) return r; r = serialize_item_escaped(f, "exec-context-root-directory", c->root_directory); if (r < 0) return r; r = serialize_item_escaped(f, "exec-context-root-image", c->root_image); if (r < 0) return r; if (c->root_image_options) { _cleanup_free_ char *options = NULL; LIST_FOREACH(mount_options, o, c->root_image_options) { if (isempty(o->options)) continue; _cleanup_free_ char *escaped = NULL; escaped = shell_escape(o->options, ":"); if (!escaped) return log_oom_debug(); if (!strextend(&options, " ", partition_designator_to_string(o->partition_designator), ":", escaped)) return log_oom_debug(); } r = serialize_item(f, "exec-context-root-image-options", options); if (r < 0) return r; } r = serialize_item(f, "exec-context-root-verity", c->root_verity); if (r < 0) return r; r = serialize_item(f, "exec-context-root-hash-path", c->root_hash_path); if (r < 0) return r; r = serialize_item(f, "exec-context-root-hash-sig-path", c->root_hash_sig_path); if (r < 0) return r; r = serialize_item_hexmem(f, "exec-context-root-hash", c->root_hash, c->root_hash_size); if (r < 0) return r; r = serialize_item_base64mem(f, "exec-context-root-hash-sig", c->root_hash_sig, c->root_hash_sig_size); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-root-ephemeral", c->root_ephemeral); if (r < 0) return r; r = serialize_item_format(f, "exec-context-umask", "%04o", c->umask); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-non-blocking", c->non_blocking); if (r < 0) return r; r = serialize_item_tristate(f, "exec-context-private-mounts", c->private_mounts); if (r < 0) return r; r = serialize_item_tristate(f, "exec-context-mount-api-vfs", c->mount_apivfs); if (r < 0) return r; r = serialize_item_tristate(f, "exec-context-memory-ksm", c->memory_ksm); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-private-tmp", c->private_tmp); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-private-devices", c->private_devices); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-protect-kernel-tunables", c->protect_kernel_tunables); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-protect-kernel-modules", c->protect_kernel_modules); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-protect-kernel-logs", c->protect_kernel_logs); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-protect-clock", c->protect_clock); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-protect-control-groups", c->protect_control_groups); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-private-network", c->private_network); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-private-users", c->private_users); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-private-ipc", c->private_ipc); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-remove-ipc", c->remove_ipc); if (r < 0) return r; r = serialize_item(f, "exec-context-protect-home", protect_home_to_string(c->protect_home)); if (r < 0) return r; r = serialize_item(f, "exec-context-protect-system", protect_system_to_string(c->protect_system)); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-same-pgrp", c->same_pgrp); if (r < 0) return r; r = serialize_bool(f, "exec-context-ignore-sigpipe", c->ignore_sigpipe); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-memory-deny-write-execute", c->memory_deny_write_execute); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-restrict-realtime", c->restrict_realtime); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-restrict-suid-sgid", c->restrict_suid_sgid); if (r < 0) return r; r = serialize_item(f, "exec-context-keyring-mode", exec_keyring_mode_to_string(c->keyring_mode)); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-protect-hostname", c->protect_hostname); if (r < 0) return r; r = serialize_item(f, "exec-context-protect-proc", protect_proc_to_string(c->protect_proc)); if (r < 0) return r; r = serialize_item(f, "exec-context-proc-subset", proc_subset_to_string(c->proc_subset)); if (r < 0) return r; r = serialize_item(f, "exec-context-runtime-directory-preserve-mode", exec_preserve_mode_to_string(c->runtime_directory_preserve_mode)); if (r < 0) return r; for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) { _cleanup_free_ char *key = NULL, *value = NULL; key = strjoin("exec-context-directories-", exec_directory_type_to_string(dt)); if (!key) return log_oom_debug(); if (asprintf(&value, "%04o", c->directories[dt].mode) < 0) return log_oom_debug(); FOREACH_ARRAY(i, c->directories[dt].items, c->directories[dt].n_items) { _cleanup_free_ char *path_escaped = NULL; path_escaped = shell_escape(i->path, ":" WHITESPACE); if (!path_escaped) return log_oom_debug(); if (!strextend(&value, " ", path_escaped)) return log_oom_debug(); if (!strextend(&value, ":", yes_no(i->only_create))) return log_oom_debug(); STRV_FOREACH(d, i->symlinks) { _cleanup_free_ char *link_escaped = NULL; link_escaped = shell_escape(*d, ":" WHITESPACE); if (!link_escaped) return log_oom_debug(); if (!strextend(&value, ":", link_escaped)) return log_oom_debug(); } } r = serialize_item(f, key, value); if (r < 0) return r; } r = serialize_usec(f, "exec-context-timeout-clean-usec", c->timeout_clean_usec); if (r < 0) return r; if (c->nice_set) { r = serialize_item_format(f, "exec-context-nice", "%i", c->nice); if (r < 0) return r; } if (c->oom_score_adjust_set) { r = serialize_item_format(f, "exec-context-oom-score-adjust", "%i", c->oom_score_adjust); if (r < 0) return r; } if (c->coredump_filter_set) { r = serialize_item_format(f, "exec-context-coredump-filter", "%"PRIx64, c->coredump_filter); if (r < 0) return r; } for (unsigned i = 0; i < RLIM_NLIMITS; i++) { _cleanup_free_ char *key = NULL, *limit = NULL; if (!c->rlimit[i]) continue; key = strjoin("exec-context-limit-", rlimit_to_string(i)); if (!key) return log_oom_debug(); r = rlimit_format(c->rlimit[i], &limit); if (r < 0) return r; r = serialize_item(f, key, limit); if (r < 0) return r; } if (c->ioprio_set) { r = serialize_item_format(f, "exec-context-ioprio", "%d", c->ioprio); if (r < 0) return r; } if (c->cpu_sched_set) { _cleanup_free_ char *policy_str = NULL; r = sched_policy_to_string_alloc(c->cpu_sched_policy, &policy_str); if (r < 0) return r; r = serialize_item(f, "exec-context-cpu-scheduling-policy", policy_str); if (r < 0) return r; r = serialize_item_format(f, "exec-context-cpu-scheduling-priority", "%i", c->cpu_sched_priority); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-cpu-scheduling-reset-on-fork", c->cpu_sched_reset_on_fork); if (r < 0) return r; } if (c->cpu_set.set) { _cleanup_free_ char *affinity = NULL; affinity = cpu_set_to_range_string(&c->cpu_set); if (!affinity) return log_oom_debug(); r = serialize_item(f, "exec-context-cpu-affinity", affinity); if (r < 0) return r; } if (mpol_is_valid(numa_policy_get_type(&c->numa_policy))) { _cleanup_free_ char *nodes = NULL; nodes = cpu_set_to_range_string(&c->numa_policy.nodes); if (!nodes) return log_oom_debug(); if (nodes) { r = serialize_item(f, "exec-context-numa-mask", nodes); if (r < 0) return r; } r = serialize_item_format(f, "exec-context-numa-policy", "%d", c->numa_policy.type); if (r < 0) return r; } r = serialize_bool_elide(f, "exec-context-cpu-affinity-from-numa", c->cpu_affinity_from_numa); if (r < 0) return r; if (c->timer_slack_nsec != NSEC_INFINITY) { r = serialize_item_format(f, "exec-context-timer-slack-nsec", NSEC_FMT, c->timer_slack_nsec); if (r < 0) return r; } r = serialize_item(f, "exec-context-std-input", exec_input_to_string(c->std_input)); if (r < 0) return r; r = serialize_item(f, "exec-context-std-output", exec_output_to_string(c->std_output)); if (r < 0) return r; r = serialize_item(f, "exec-context-std-error", exec_output_to_string(c->std_error)); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-stdio-as-fds", c->stdio_as_fds); if (r < 0) return r; switch (c->std_input) { case EXEC_INPUT_NAMED_FD: r = serialize_item(f, "exec-context-std-input-fd-name", c->stdio_fdname[STDIN_FILENO]); if (r < 0) return r; break; case EXEC_INPUT_FILE: r = serialize_item(f, "exec-context-std-input-file", c->stdio_file[STDIN_FILENO]); if (r < 0) return r; break; default: break; } r = serialize_std_out_err(c, f, STDOUT_FILENO); if (r < 0) return r; r = serialize_std_out_err(c, f, STDERR_FILENO); if (r < 0) return r; r = serialize_item_base64mem(f, "exec-context-stdin-data", c->stdin_data, c->stdin_data_size); if (r < 0) return r; r = serialize_item(f, "exec-context-tty-path", c->tty_path); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-tty-reset", c->tty_reset); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-tty-vhangup", c->tty_vhangup); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-tty-vt-disallocate", c->tty_vt_disallocate); if (r < 0) return r; r = serialize_item_format(f, "exec-context-tty-rows", "%u", c->tty_rows); if (r < 0) return r; r = serialize_item_format(f, "exec-context-tty-columns", "%u", c->tty_cols); if (r < 0) return r; r = serialize_item_format(f, "exec-context-syslog-priority", "%i", c->syslog_priority); if (r < 0) return r; r = serialize_bool(f, "exec-context-syslog-level-prefix", c->syslog_level_prefix); if (r < 0) return r; r = serialize_item(f, "exec-context-syslog-identifier", c->syslog_identifier); if (r < 0) return r; /* This is also passed to executor as an argument. So, the information should be redundant in general. * But, let's keep this as is for consistency with other elements of ExecContext. See exec_spawn(). */ r = serialize_item_format(f, "exec-context-log-level-max", "%d", c->log_level_max); if (r < 0) return r; if (c->log_ratelimit_interval_usec > 0) { r = serialize_usec(f, "exec-context-log-ratelimit-interval-usec", c->log_ratelimit_interval_usec); if (r < 0) return r; } if (c->log_ratelimit_burst > 0) { r = serialize_item_format(f, "exec-context-log-ratelimit-burst", "%u", c->log_ratelimit_burst); if (r < 0) return r; } r = serialize_string_set(f, "exec-context-log-filter-allowed-patterns", c->log_filter_allowed_patterns); if (r < 0) return r; r = serialize_string_set(f, "exec-context-log-filter-denied-patterns", c->log_filter_denied_patterns); if (r < 0) return r; FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields) { r = serialize_item(f, "exec-context-log-extra-fields", field->iov_base); if (r < 0) return r; } r = serialize_item(f, "exec-context-log-namespace", c->log_namespace); if (r < 0) return r; if (c->secure_bits != 0) { r = serialize_item_format(f, "exec-context-secure-bits", "%d", c->secure_bits); if (r < 0) return r; } if (c->capability_bounding_set != CAP_MASK_UNSET) { r = serialize_item_format(f, "exec-context-capability-bounding-set", "%" PRIu64, c->capability_bounding_set); if (r < 0) return r; } if (c->capability_ambient_set != 0) { r = serialize_item_format(f, "exec-context-capability-ambient-set", "%" PRIu64, c->capability_ambient_set); if (r < 0) return r; } if (c->user) { r = serialize_item(f, "exec-context-user", c->user); if (r < 0) return r; } r = serialize_item(f, "exec-context-group", c->group); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-dynamic-user", c->dynamic_user); if (r < 0) return r; r = serialize_strv(f, "exec-context-supplementary-groups", c->supplementary_groups); if (r < 0) return r; r = serialize_item_tristate(f, "exec-context-set-login-environment", c->set_login_environment); if (r < 0) return r; r = serialize_item(f, "exec-context-pam-name", c->pam_name); if (r < 0) return r; r = serialize_strv(f, "exec-context-read-write-paths", c->read_write_paths); if (r < 0) return r; r = serialize_strv(f, "exec-context-read-only-paths", c->read_only_paths); if (r < 0) return r; r = serialize_strv(f, "exec-context-inaccessible-paths", c->inaccessible_paths); if (r < 0) return r; r = serialize_strv(f, "exec-context-exec-paths", c->exec_paths); if (r < 0) return r; r = serialize_strv(f, "exec-context-no-exec-paths", c->no_exec_paths); if (r < 0) return r; r = serialize_strv(f, "exec-context-exec-search-path", c->exec_search_path); if (r < 0) return r; r = serialize_item_format(f, "exec-context-mount-propagation-flag", "%lu", c->mount_propagation_flag); if (r < 0) return r; FOREACH_ARRAY(mount, c->bind_mounts, c->n_bind_mounts) { _cleanup_free_ char *src_escaped = NULL, *dst_escaped = NULL; src_escaped = shell_escape(mount->source, ":" WHITESPACE); if (!src_escaped) return log_oom_debug(); dst_escaped = shell_escape(mount->destination, ":" WHITESPACE); if (!dst_escaped) return log_oom_debug(); r = serialize_item_format(f, mount->read_only ? "exec-context-bind-read-only-path" : "exec-context-bind-path", "%s%s:%s:%s", mount->ignore_enoent ? "-" : "", src_escaped, dst_escaped, mount->recursive ? "rbind" : "norbind"); if (r < 0) return r; } FOREACH_ARRAY(tmpfs, c->temporary_filesystems, c->n_temporary_filesystems) { _cleanup_free_ char *escaped = NULL; if (!isempty(tmpfs->options)) { escaped = shell_escape(tmpfs->options, ":"); if (!escaped) return log_oom_debug(); } r = serialize_item_format(f, "exec-context-temporary-filesystems", "%s%s%s", tmpfs->path, isempty(escaped) ? "" : ":", strempty(escaped)); if (r < 0) return r; } r = serialize_item(f, "exec-context-utmp-id", c->utmp_id); if (r < 0) return r; r = serialize_item(f, "exec-context-utmp-mode", exec_utmp_mode_to_string(c->utmp_mode)); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-no-new-privileges", c->no_new_privileges); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-selinux-context-ignore", c->selinux_context_ignore); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-apparmor-profile-ignore", c->apparmor_profile_ignore); if (r < 0) return r; r = serialize_bool_elide(f, "exec-context-smack-process-label-ignore", c->smack_process_label_ignore); if (r < 0) return r; if (c->selinux_context) { r = serialize_item_format(f, "exec-context-selinux-context", "%s%s", c->selinux_context_ignore ? "-" : "", c->selinux_context); if (r < 0) return r; } if (c->apparmor_profile) { r = serialize_item_format(f, "exec-context-apparmor-profile", "%s%s", c->apparmor_profile_ignore ? "-" : "", c->apparmor_profile); if (r < 0) return r; } if (c->smack_process_label) { r = serialize_item_format(f, "exec-context-smack-process-label", "%s%s", c->smack_process_label_ignore ? "-" : "", c->smack_process_label); if (r < 0) return r; } if (c->personality != PERSONALITY_INVALID) { r = serialize_item(f, "exec-context-personality", personality_to_string(c->personality)); if (r < 0) return r; } r = serialize_bool_elide(f, "exec-context-lock-personality", c->lock_personality); if (r < 0) return r; #if HAVE_SECCOMP if (!hashmap_isempty(c->syscall_filter)) { void *errno_num, *id; HASHMAP_FOREACH_KEY(errno_num, id, c->syscall_filter) { r = serialize_item_format(f, "exec-context-syscall-filter", "%d %d", PTR_TO_INT(id) - 1, PTR_TO_INT(errno_num)); if (r < 0) return r; } } if (!set_isempty(c->syscall_archs)) { void *id; SET_FOREACH(id, c->syscall_archs) { r = serialize_item_format(f, "exec-context-syscall-archs", "%u", PTR_TO_UINT(id) - 1); if (r < 0) return r; } } if (c->syscall_errno > 0) { r = serialize_item_format(f, "exec-context-syscall-errno", "%d", c->syscall_errno); if (r < 0) return r; } r = serialize_bool_elide(f, "exec-context-syscall-allow-list", c->syscall_allow_list); if (r < 0) return r; if (!hashmap_isempty(c->syscall_log)) { void *errno_num, *id; HASHMAP_FOREACH_KEY(errno_num, id, c->syscall_log) { r = serialize_item_format(f, "exec-context-syscall-log", "%d %d", PTR_TO_INT(id) - 1, PTR_TO_INT(errno_num)); if (r < 0) return r; } } r = serialize_bool_elide(f, "exec-context-syscall-log-allow-list", c->syscall_log_allow_list); if (r < 0) return r; #endif if (c->restrict_namespaces != NAMESPACE_FLAGS_INITIAL) { r = serialize_item_format(f, "exec-context-restrict-namespaces", "%lu", c->restrict_namespaces); if (r < 0) return r; } #if HAVE_LIBBPF if (exec_context_restrict_filesystems_set(c)) { char *fs; SET_FOREACH(fs, c->restrict_filesystems) { r = serialize_item(f, "exec-context-restrict-filesystems", fs); if (r < 0) return r; } } r = serialize_bool_elide(f, "exec-context-restrict-filesystems-allow-list", c->restrict_filesystems_allow_list); if (r < 0) return r; #endif if (!set_isempty(c->address_families)) { void *afp; SET_FOREACH(afp, c->address_families) { int af = PTR_TO_INT(afp); if (af <= 0 || af >= af_max()) continue; r = serialize_item_format(f, "exec-context-address-families", "%d", af); if (r < 0) return r; } } r = serialize_bool_elide(f, "exec-context-address-families-allow-list", c->address_families_allow_list); if (r < 0) return r; r = serialize_item(f, "exec-context-network-namespace-path", c->network_namespace_path); if (r < 0) return r; r = serialize_item(f, "exec-context-ipc-namespace-path", c->ipc_namespace_path); if (r < 0) return r; FOREACH_ARRAY(mount, c->mount_images, c->n_mount_images) { _cleanup_free_ char *s = NULL, *source_escaped = NULL, *dest_escaped = NULL; source_escaped = shell_escape(mount->source, WHITESPACE); if (!source_escaped) return log_oom_debug(); dest_escaped = shell_escape(mount->destination, WHITESPACE); if (!dest_escaped) return log_oom_debug(); s = strjoin(mount->ignore_enoent ? "-" : "", source_escaped, " ", dest_escaped); if (!s) return log_oom_debug(); LIST_FOREACH(mount_options, o, mount->mount_options) { _cleanup_free_ char *escaped = NULL; if (isempty(o->options)) continue; escaped = shell_escape(o->options, ":"); if (!escaped) return log_oom_debug(); if (!strextend(&s, " ", partition_designator_to_string(o->partition_designator), ":", escaped)) return log_oom_debug(); } r = serialize_item(f, "exec-context-mount-image", s); if (r < 0) return r; } FOREACH_ARRAY(mount, c->extension_images, c->n_extension_images) { _cleanup_free_ char *s = NULL, *source_escaped = NULL; source_escaped = shell_escape(mount->source, ":" WHITESPACE); if (!source_escaped) return log_oom_debug(); s = strjoin(mount->ignore_enoent ? "-" : "", source_escaped); if (!s) return log_oom_debug(); LIST_FOREACH(mount_options, o, mount->mount_options) { _cleanup_free_ char *escaped = NULL; if (isempty(o->options)) continue; escaped = shell_escape(o->options, ":"); if (!escaped) return log_oom_debug(); if (!strextend(&s, " ", partition_designator_to_string(o->partition_designator), ":", escaped)) return log_oom_debug(); } r = serialize_item(f, "exec-context-extension-image", s); if (r < 0) return r; } r = serialize_strv(f, "exec-context-extension-directories", c->extension_directories); if (r < 0) return r; ExecSetCredential *sc; HASHMAP_FOREACH(sc, c->set_credentials) { _cleanup_free_ char *data = NULL; if (base64mem(sc->data, sc->size, &data) < 0) return log_oom_debug(); r = serialize_item_format(f, "exec-context-set-credentials", "%s %s %s", sc->id, data, yes_no(sc->encrypted)); if (r < 0) return r; } ExecLoadCredential *lc; HASHMAP_FOREACH(lc, c->load_credentials) { r = serialize_item_format(f, "exec-context-load-credentials", "%s %s %s", lc->id, lc->path, yes_no(lc->encrypted)); if (r < 0) return r; } if (!set_isempty(c->import_credentials)) { char *ic; SET_FOREACH(ic, c->import_credentials) { r = serialize_item(f, "exec-context-import-credentials", ic); if (r < 0) return r; } } r = serialize_image_policy(f, "exec-context-root-image-policy", c->root_image_policy); if (r < 0) return r; r = serialize_image_policy(f, "exec-context-mount-image-policy", c->mount_image_policy); if (r < 0) return r; r = serialize_image_policy(f, "exec-context-extension-image-policy", c->extension_image_policy); if (r < 0) return r; fputc('\n', f); /* End marker */ return 0; } static int exec_context_deserialize(ExecContext *c, FILE *f) { int r; assert(f); if (!c) return 0; for (;;) { _cleanup_free_ char *l = NULL; const char *val; r = deserialize_read_line(f, &l); if (r < 0) return r; if (r == 0) /* eof or end marker */ break; if ((val = startswith(l, "exec-context-environment="))) { r = deserialize_strv(val, &c->environment); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-environment-files="))) { r = deserialize_strv(val, &c->environment_files); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-pass-environment="))) { r = deserialize_strv(val, &c->pass_environment); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-unset-environment="))) { r = deserialize_strv(val, &c->unset_environment); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-working-directory="))) { ssize_t k; char *p; k = cunescape(val, 0, &p); if (k < 0) return k; free_and_replace(c->working_directory, p); } else if ((val = startswith(l, "exec-context-root-directory="))) { ssize_t k; char *p; k = cunescape(val, 0, &p); if (k < 0) return k; free_and_replace(c->root_directory, p); } else if ((val = startswith(l, "exec-context-root-image="))) { ssize_t k; char *p; k = cunescape(val, 0, &p); if (k < 0) return k; free_and_replace(c->root_image, p); } else if ((val = startswith(l, "exec-context-root-image-options="))) { for (;;) { _cleanup_free_ char *word = NULL, *mount_options = NULL, *partition = NULL; PartitionDesignator partition_designator; MountOptions *o = NULL; const char *p; r = extract_first_word(&val, &word, NULL, 0); if (r < 0) return r; if (r == 0) break; p = word; r = extract_many_words(&p, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options); if (r < 0) return r; if (r == 0) continue; partition_designator = partition_designator_from_string(partition); if (partition_designator < 0) return -EINVAL; o = new(MountOptions, 1); if (!o) return log_oom_debug(); *o = (MountOptions) { .partition_designator = partition_designator, .options = TAKE_PTR(mount_options), }; LIST_APPEND(mount_options, c->root_image_options, o); } } else if ((val = startswith(l, "exec-context-root-verity="))) { r = free_and_strdup(&c->root_verity, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-root-hash-path="))) { r = free_and_strdup(&c->root_hash_path, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-root-hash-sig-path="))) { r = free_and_strdup(&c->root_hash_sig_path, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-root-hash="))) { c->root_hash = mfree(c->root_hash); r = unhexmem(val, &c->root_hash, &c->root_hash_size); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-root-hash-sig="))) { c->root_hash_sig = mfree(c->root_hash_sig); r= unbase64mem(val, &c->root_hash_sig, &c->root_hash_sig_size); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-root-ephemeral="))) { r = parse_boolean(val); if (r < 0) return r; c->root_ephemeral = r; } else if ((val = startswith(l, "exec-context-umask="))) { r = parse_mode(val, &c->umask); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-private-non-blocking="))) { r = parse_boolean(val); if (r < 0) return r; c->non_blocking = r; } else if ((val = startswith(l, "exec-context-private-mounts="))) { r = safe_atoi(val, &c->private_mounts); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-mount-api-vfs="))) { r = safe_atoi(val, &c->mount_apivfs); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-memory-ksm="))) { r = safe_atoi(val, &c->memory_ksm); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-private-tmp="))) { r = parse_boolean(val); if (r < 0) return r; c->private_tmp = r; } else if ((val = startswith(l, "exec-context-private-devices="))) { r = parse_boolean(val); if (r < 0) return r; c->private_devices = r; } else if ((val = startswith(l, "exec-context-protect-kernel-tunables="))) { r = parse_boolean(val); if (r < 0) return r; c->protect_kernel_tunables = r; } else if ((val = startswith(l, "exec-context-protect-kernel-modules="))) { r = parse_boolean(val); if (r < 0) return r; c->protect_kernel_modules = r; } else if ((val = startswith(l, "exec-context-protect-kernel-logs="))) { r = parse_boolean(val); if (r < 0) return r; c->protect_kernel_logs = r; } else if ((val = startswith(l, "exec-context-protect-clock="))) { r = parse_boolean(val); if (r < 0) return r; c->protect_clock = r; } else if ((val = startswith(l, "exec-context-protect-control-groups="))) { r = parse_boolean(val); if (r < 0) return r; c->protect_control_groups = r; } else if ((val = startswith(l, "exec-context-private-network="))) { r = parse_boolean(val); if (r < 0) return r; c->private_network = r; } else if ((val = startswith(l, "exec-context-private-users="))) { r = parse_boolean(val); if (r < 0) return r; c->private_users = r; } else if ((val = startswith(l, "exec-context-private-ipc="))) { r = parse_boolean(val); if (r < 0) return r; c->private_ipc = r; } else if ((val = startswith(l, "exec-context-remove-ipc="))) { r = parse_boolean(val); if (r < 0) return r; c->remove_ipc = r; } else if ((val = startswith(l, "exec-context-protect-home="))) { c->protect_home = protect_home_from_string(val); if (c->protect_home < 0) return -EINVAL; } else if ((val = startswith(l, "exec-context-protect-system="))) { c->protect_system = protect_system_from_string(val); if (c->protect_system < 0) return -EINVAL; } else if ((val = startswith(l, "exec-context-same-pgrp="))) { r = parse_boolean(val); if (r < 0) return r; c->same_pgrp = r; } else if ((val = startswith(l, "exec-context-non-blocking="))) { r = parse_boolean(val); if (r < 0) return r; c->non_blocking = r; } else if ((val = startswith(l, "exec-context-ignore-sigpipe="))) { r = parse_boolean(val); if (r < 0) return r; c->ignore_sigpipe = r; } else if ((val = startswith(l, "exec-context-memory-deny-write-execute="))) { r = parse_boolean(val); if (r < 0) return r; c->memory_deny_write_execute = r; } else if ((val = startswith(l, "exec-context-restrict-realtime="))) { r = parse_boolean(val); if (r < 0) return r; c->restrict_realtime = r; } else if ((val = startswith(l, "exec-context-restrict-suid-sgid="))) { r = parse_boolean(val); if (r < 0) return r; c->restrict_suid_sgid = r; } else if ((val = startswith(l, "exec-context-keyring-mode="))) { c->keyring_mode = exec_keyring_mode_from_string(val); if (c->keyring_mode < 0) return -EINVAL; } else if ((val = startswith(l, "exec-context-protect-hostname="))) { r = parse_boolean(val); if (r < 0) return r; c->protect_hostname = r; } else if ((val = startswith(l, "exec-context-protect-proc="))) { c->protect_proc = protect_proc_from_string(val); if (c->protect_proc < 0) return -EINVAL; } else if ((val = startswith(l, "exec-context-proc-subset="))) { c->proc_subset = proc_subset_from_string(val); if (c->proc_subset < 0) return -EINVAL; } else if ((val = startswith(l, "exec-context-runtime-directory-preserve-mode="))) { c->runtime_directory_preserve_mode = exec_preserve_mode_from_string(val); if (c->runtime_directory_preserve_mode < 0) return -EINVAL; } else if ((val = startswith(l, "exec-context-directories-"))) { _cleanup_free_ char *type = NULL, *mode = NULL; ExecDirectoryType dt; r = extract_many_words(&val, "= ", 0, &type, &mode); if (r < 0) return r; if (r == 0 || !mode) return -EINVAL; dt = exec_directory_type_from_string(type); if (dt < 0) return -EINVAL; r = parse_mode(mode, &c->directories[dt].mode); if (r < 0) return r; for (;;) { _cleanup_free_ char *tuple = NULL, *path = NULL, *only_create = NULL; const char *p; /* Use EXTRACT_UNESCAPE_RELAX here, as we unescape the colons in subsequent calls */ r = extract_first_word(&val, &tuple, WHITESPACE, EXTRACT_UNESCAPE_SEPARATORS|EXTRACT_UNESCAPE_RELAX); if (r < 0) return r; if (r == 0) break; p = tuple; r = extract_many_words(&p, ":", EXTRACT_UNESCAPE_SEPARATORS, &path, &only_create); if (r < 0) return r; if (r < 2) continue; r = exec_directory_add(&c->directories[dt], path, NULL); if (r < 0) return r; r = parse_boolean(only_create); if (r < 0) return r; c->directories[dt].items[c->directories[dt].n_items - 1].only_create = r; if (isempty(p)) continue; for (;;) { _cleanup_free_ char *link = NULL; r = extract_first_word(&p, &link, ":", EXTRACT_UNESCAPE_SEPARATORS); if (r < 0) return r; if (r == 0) break; r = strv_consume(&c->directories[dt].items[c->directories[dt].n_items - 1].symlinks, TAKE_PTR(link)); if (r < 0) return r; } } } else if ((val = startswith(l, "exec-context-timeout-clean-usec="))) { r = deserialize_usec(val, &c->timeout_clean_usec); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-nice="))) { r = safe_atoi(val, &c->nice); if (r < 0) return r; c->nice_set = true; } else if ((val = startswith(l, "exec-context-working-directory-missing-ok="))) { r = parse_boolean(val); if (r < 0) return r; c->working_directory_missing_ok = r; } else if ((val = startswith(l, "exec-context-working-directory-home="))) { r = parse_boolean(val); if (r < 0) return r; c->working_directory_home = r; } else if ((val = startswith(l, "exec-context-oom-score-adjust="))) { r = safe_atoi(val, &c->oom_score_adjust); if (r < 0) return r; c->oom_score_adjust_set = true; } else if ((val = startswith(l, "exec-context-coredump-filter="))) { r = safe_atoux64(val, &c->coredump_filter); if (r < 0) return r; c->coredump_filter_set = true; } else if ((val = startswith(l, "exec-context-limit-"))) { _cleanup_free_ struct rlimit *rlimit = NULL; _cleanup_free_ char *limit = NULL; int type; r = extract_first_word(&val, &limit, "=", 0); if (r < 0) return r; if (r == 0 || !val) return -EINVAL; type = rlimit_from_string(limit); if (type < 0) return -EINVAL; if (!c->rlimit[type]) { rlimit = new0(struct rlimit, 1); if (!rlimit) return log_oom_debug(); r = rlimit_parse(type, val, rlimit); if (r < 0) return r; c->rlimit[type] = TAKE_PTR(rlimit); } else { r = rlimit_parse(type, val, c->rlimit[type]); if (r < 0) return r; } } else if ((val = startswith(l, "exec-context-ioprio="))) { r = safe_atoi(val, &c->ioprio); if (r < 0) return r; c->ioprio_set = true; } else if ((val = startswith(l, "exec-context-cpu-scheduling-policy="))) { c->cpu_sched_policy = sched_policy_from_string(val); if (c->cpu_sched_policy < 0) return -EINVAL; c->cpu_sched_set = true; } else if ((val = startswith(l, "exec-context-cpu-scheduling-priority="))) { r = safe_atoi(val, &c->cpu_sched_priority); if (r < 0) return r; c->cpu_sched_set = true; } else if ((val = startswith(l, "exec-context-cpu-scheduling-reset-on-fork="))) { r = parse_boolean(val); if (r < 0) return r; c->cpu_sched_reset_on_fork = r; c->cpu_sched_set = true; } else if ((val = startswith(l, "exec-context-cpu-affinity="))) { if (c->cpu_set.set) return -EINVAL; /* duplicated */ r = parse_cpu_set(val, &c->cpu_set); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-numa-mask="))) { if (c->numa_policy.nodes.set) return -EINVAL; /* duplicated */ r = parse_cpu_set(val, &c->numa_policy.nodes); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-numa-policy="))) { r = safe_atoi(val, &c->numa_policy.type); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-cpu-affinity-from-numa="))) { r = parse_boolean(val); if (r < 0) return r; c->cpu_affinity_from_numa = r; } else if ((val = startswith(l, "exec-context-timer-slack-nsec="))) { r = deserialize_usec(val, (usec_t *)&c->timer_slack_nsec); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-std-input="))) { c->std_input = exec_input_from_string(val); if (c->std_input < 0) return c->std_input; } else if ((val = startswith(l, "exec-context-std-output="))) { c->std_output = exec_output_from_string(val); if (c->std_output < 0) return c->std_output; } else if ((val = startswith(l, "exec-context-std-error="))) { c->std_error = exec_output_from_string(val); if (c->std_error < 0) return c->std_error; } else if ((val = startswith(l, "exec-context-stdio-as-fds="))) { r = parse_boolean(val); if (r < 0) return r; c->stdio_as_fds = r; } else if ((val = startswith(l, "exec-context-std-input-fd-name="))) { r = free_and_strdup(&c->stdio_fdname[STDIN_FILENO], val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-std-output-fd-name="))) { r = free_and_strdup(&c->stdio_fdname[STDOUT_FILENO], val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-std-error-fd-name="))) { r = free_and_strdup(&c->stdio_fdname[STDERR_FILENO], val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-std-input-file="))) { r = free_and_strdup(&c->stdio_file[STDIN_FILENO], val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-std-output-file="))) { r = free_and_strdup(&c->stdio_file[STDOUT_FILENO], val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-std-output-file-append="))) { r = free_and_strdup(&c->stdio_file[STDOUT_FILENO], val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-std-output-file-truncate="))) { r = free_and_strdup(&c->stdio_file[STDOUT_FILENO], val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-std-error-file="))) { r = free_and_strdup(&c->stdio_file[STDERR_FILENO], val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-std-error-file-append="))) { r = free_and_strdup(&c->stdio_file[STDERR_FILENO], val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-std-error-file-truncate="))) { r = free_and_strdup(&c->stdio_file[STDERR_FILENO], val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-stdin-data="))) { if (c->stdin_data) return -EINVAL; /* duplicated */ r = unbase64mem(val, &c->stdin_data, &c->stdin_data_size); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-tty-path="))) { r = free_and_strdup(&c->tty_path, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-tty-reset="))) { r = parse_boolean(val); if (r < 0) return r; c->tty_reset = r; } else if ((val = startswith(l, "exec-context-tty-vhangup="))) { r = parse_boolean(val); if (r < 0) return r; c->tty_vhangup = r; } else if ((val = startswith(l, "exec-context-tty-vt-disallocate="))) { r = parse_boolean(val); if (r < 0) return r; c->tty_vt_disallocate = r; } else if ((val = startswith(l, "exec-context-tty-rows="))) { r = safe_atou(val, &c->tty_rows); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-tty-columns="))) { r = safe_atou(val, &c->tty_cols); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-syslog-priority="))) { r = safe_atoi(val, &c->syslog_priority); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-syslog-level-prefix="))) { r = parse_boolean(val); if (r < 0) return r; c->syslog_level_prefix = r; } else if ((val = startswith(l, "exec-context-syslog-identifier="))) { r = free_and_strdup(&c->syslog_identifier, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-log-level-max="))) { /* See comment in serialization. */ r = safe_atoi(val, &c->log_level_max); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-log-ratelimit-interval-usec="))) { r = deserialize_usec(val, &c->log_ratelimit_interval_usec); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-log-ratelimit-burst="))) { r = safe_atou(val, &c->log_ratelimit_burst); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-log-filter-allowed-patterns="))) { r = set_put_strdup(&c->log_filter_allowed_patterns, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-log-filter-denied-patterns="))) { r = set_put_strdup(&c->log_filter_denied_patterns, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-log-extra-fields="))) { if (!GREEDY_REALLOC(c->log_extra_fields, c->n_log_extra_fields + 1)) return log_oom_debug(); c->log_extra_fields[c->n_log_extra_fields++].iov_base = strdup(val); if (!c->log_extra_fields[c->n_log_extra_fields-1].iov_base) return log_oom_debug(); } else if ((val = startswith(l, "exec-context-log-namespace="))) { r = free_and_strdup(&c->log_namespace, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-secure-bits="))) { r = safe_atoi(val, &c->secure_bits); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-capability-bounding-set="))) { r = safe_atou64(val, &c->capability_bounding_set); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-capability-ambient-set="))) { r = safe_atou64(val, &c->capability_ambient_set); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-user="))) { r = free_and_strdup(&c->user, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-group="))) { r = free_and_strdup(&c->group, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-dynamic-user="))) { r = parse_boolean(val); if (r < 0) return r; c->dynamic_user = r; } else if ((val = startswith(l, "exec-context-supplementary-groups="))) { r = deserialize_strv(val, &c->supplementary_groups); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-set-login-environment="))) { r = safe_atoi(val, &c->set_login_environment); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-pam-name="))) { r = free_and_strdup(&c->pam_name, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-read-write-paths="))) { r = deserialize_strv(val, &c->read_write_paths); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-read-only-paths="))) { r = deserialize_strv(val, &c->read_only_paths); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-inaccessible-paths="))) { r = deserialize_strv(val, &c->inaccessible_paths); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-exec-paths="))) { r = deserialize_strv(val, &c->exec_paths); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-no-exec-paths="))) { r = deserialize_strv(val, &c->no_exec_paths); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-exec-search-path="))) { r = deserialize_strv(val, &c->exec_search_path); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-mount-propagation-flag="))) { r = safe_atolu(val, &c->mount_propagation_flag); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-bind-read-only-path="))) { _cleanup_free_ char *source = NULL, *destination = NULL; bool rbind = true, ignore_enoent = false; char *s = NULL, *d = NULL; r = extract_first_word(&val, &source, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_UNESCAPE_SEPARATORS); if (r < 0) return r; if (r == 0) return -EINVAL; s = source; if (s[0] == '-') { ignore_enoent = true; s++; } if (val && val[-1] == ':') { r = extract_first_word(&val, &destination, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_UNESCAPE_SEPARATORS); if (r < 0) return r; if (r == 0) continue; d = destination; if (val && val[-1] == ':') { _cleanup_free_ char *options = NULL; r = extract_first_word(&val, &options, NULL, EXTRACT_UNQUOTE); if (r < 0) return -r; if (isempty(options) || streq(options, "rbind")) rbind = true; else if (streq(options, "norbind")) rbind = false; else continue; } } else d = s; r = bind_mount_add(&c->bind_mounts, &c->n_bind_mounts, &(BindMount) { .source = s, .destination = d, .read_only = true, .recursive = rbind, .ignore_enoent = ignore_enoent, }); if (r < 0) return log_oom_debug(); } else if ((val = startswith(l, "exec-context-bind-path="))) { _cleanup_free_ char *source = NULL, *destination = NULL; bool rbind = true, ignore_enoent = false; char *s = NULL, *d = NULL; r = extract_first_word(&val, &source, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_UNESCAPE_SEPARATORS); if (r < 0) return r; if (r == 0) return -EINVAL; s = source; if (s[0] == '-') { ignore_enoent = true; s++; } if (val && val[-1] == ':') { r = extract_first_word(&val, &destination, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_UNESCAPE_SEPARATORS); if (r < 0) return r; if (r == 0) continue; d = destination; if (val && val[-1] == ':') { _cleanup_free_ char *options = NULL; r = extract_first_word(&val, &options, NULL, EXTRACT_UNQUOTE); if (r < 0) return -r; if (isempty(options) || streq(options, "rbind")) rbind = true; else if (streq(options, "norbind")) rbind = false; else continue; } } else d = s; r = bind_mount_add(&c->bind_mounts, &c->n_bind_mounts, &(BindMount) { .source = s, .destination = d, .read_only = false, .recursive = rbind, .ignore_enoent = ignore_enoent, }); if (r < 0) return log_oom_debug(); } else if ((val = startswith(l, "exec-context-temporary-filesystems="))) { _cleanup_free_ char *path = NULL, *options = NULL; r = extract_many_words(&val, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &path, &options); if (r < 0) return r; if (r < 1) continue; r = temporary_filesystem_add(&c->temporary_filesystems, &c->n_temporary_filesystems, path, options); if (r < 0) return log_oom_debug(); } else if ((val = startswith(l, "exec-context-utmp-id="))) { r = free_and_strdup(&c->utmp_id, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-utmp-mode="))) { c->utmp_mode = exec_utmp_mode_from_string(val); if (c->utmp_mode < 0) return c->utmp_mode; } else if ((val = startswith(l, "exec-context-no-new-privileges="))) { r = parse_boolean(val); if (r < 0) return r; c->no_new_privileges = r; } else if ((val = startswith(l, "exec-context-selinux-context-ignore="))) { r = parse_boolean(val); if (r < 0) return r; c->selinux_context_ignore = r; } else if ((val = startswith(l, "exec-context-apparmor-profile-ignore="))) { r = parse_boolean(val); if (r < 0) return r; c->apparmor_profile_ignore = r; } else if ((val = startswith(l, "exec-context-smack-process-label-ignore="))) { r = parse_boolean(val); if (r < 0) return r; c->smack_process_label_ignore = r; } else if ((val = startswith(l, "exec-context-selinux-context="))) { if (val[0] == '-') { c->selinux_context_ignore = true; val++; } r = free_and_strdup(&c->selinux_context, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-apparmor-profile="))) { if (val[0] == '-') { c->apparmor_profile_ignore = true; val++; } r = free_and_strdup(&c->apparmor_profile, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-smack-process-label="))) { if (val[0] == '-') { c->smack_process_label_ignore = true; val++; } r = free_and_strdup(&c->smack_process_label, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-personality="))) { c->personality = personality_from_string(val); if (c->personality == PERSONALITY_INVALID) return -EINVAL; } else if ((val = startswith(l, "exec-context-lock-personality="))) { r = parse_boolean(val); if (r < 0) return r; c->lock_personality = r; #if HAVE_SECCOMP } else if ((val = startswith(l, "exec-context-syscall-filter="))) { _cleanup_free_ char *s_id = NULL, *s_errno_num = NULL; int id, errno_num; r = extract_many_words(&val, NULL, 0, &s_id, &s_errno_num); if (r < 0) return r; if (r != 2) continue; r = safe_atoi(s_id, &id); if (r < 0) return r; r = safe_atoi(s_errno_num, &errno_num); if (r < 0) return r; r = hashmap_ensure_put(&c->syscall_filter, NULL, INT_TO_PTR(id + 1), INT_TO_PTR(errno_num)); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-syscall-archs="))) { unsigned int id; r = safe_atou(val, &id); if (r < 0) return r; r = set_ensure_put(&c->syscall_archs, NULL, UINT_TO_PTR(id + 1)); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-syscall-errno="))) { r = safe_atoi(val, &c->syscall_errno); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-syscall-allow-list="))) { r = parse_boolean(val); if (r < 0) return r; c->syscall_allow_list = r; } else if ((val = startswith(l, "exec-context-syscall-log="))) { _cleanup_free_ char *s_id = NULL, *s_errno_num = NULL; int id, errno_num; r = extract_many_words(&val, " ", 0, &s_id, &s_errno_num); if (r < 0) return r; if (r != 2) continue; r = safe_atoi(s_id, &id); if (r < 0) return r; r = safe_atoi(s_errno_num, &errno_num); if (r < 0) return r; r = hashmap_ensure_put(&c->syscall_log, NULL, INT_TO_PTR(id + 1), INT_TO_PTR(errno_num)); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-syscall-log-allow-list="))) { r = parse_boolean(val); if (r < 0) return r; c->syscall_log_allow_list = r; #endif } else if ((val = startswith(l, "exec-context-restrict-namespaces="))) { r = safe_atolu(val, &c->restrict_namespaces); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-restrict-filesystems="))) { r = set_ensure_allocated(&c->restrict_filesystems, &string_hash_ops); if (r < 0) return r; r = set_put_strdup(&c->restrict_filesystems, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-restrict-filesystems-allow-list="))) { r = parse_boolean(val); if (r < 0) return r; c->restrict_filesystems_allow_list = r; } else if ((val = startswith(l, "exec-context-address-families="))) { int af; r = safe_atoi(val, &af); if (r < 0) return r; r = set_ensure_put(&c->address_families, NULL, INT_TO_PTR(af)); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-address-families-allow-list="))) { r = parse_boolean(val); if (r < 0) return r; c->address_families_allow_list = r; } else if ((val = startswith(l, "exec-context-network-namespace-path="))) { r = free_and_strdup(&c->network_namespace_path, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-ipc-namespace-path="))) { r = free_and_strdup(&c->ipc_namespace_path, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-mount-image="))) { _cleanup_(mount_options_free_allp) MountOptions *options = NULL; _cleanup_free_ char *source = NULL, *destination = NULL; bool permissive = false; char *s; r = extract_many_words(&val, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &source, &destination); if (r < 0) return r; if (r == 0) return -EINVAL; s = source; if (s[0] == '-') { permissive = true; s++; } if (isempty(destination)) continue; for (;;) { _cleanup_free_ char *tuple = NULL, *partition = NULL, *opts = NULL; PartitionDesignator partition_designator; MountOptions *o = NULL; const char *p; r = extract_first_word(&val, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE); if (r < 0) return r; if (r == 0) break; p = tuple; r = extract_many_words(&p, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &opts); if (r < 0) return r; if (r == 0) continue; if (r == 1) { o = new(MountOptions, 1); if (!o) return log_oom_debug(); *o = (MountOptions) { .partition_designator = PARTITION_ROOT, .options = TAKE_PTR(partition), }; LIST_APPEND(mount_options, options, o); continue; } partition_designator = partition_designator_from_string(partition); if (partition_designator < 0) continue; o = new(MountOptions, 1); if (!o) return log_oom_debug(); *o = (MountOptions) { .partition_designator = partition_designator, .options = TAKE_PTR(opts), }; LIST_APPEND(mount_options, options, o); } r = mount_image_add(&c->mount_images, &c->n_mount_images, &(MountImage) { .source = s, .destination = destination, .mount_options = options, .ignore_enoent = permissive, .type = MOUNT_IMAGE_DISCRETE, }); if (r < 0) return log_oom_debug(); } else if ((val = startswith(l, "exec-context-extension-image="))) { _cleanup_(mount_options_free_allp) MountOptions *options = NULL; _cleanup_free_ char *source = NULL; bool permissive = false; char *s; r = extract_first_word(&val, &source, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS); if (r < 0) return r; if (r == 0) return -EINVAL; s = source; if (s[0] == '-') { permissive = true; s++; } for (;;) { _cleanup_free_ char *tuple = NULL, *partition = NULL, *opts = NULL; PartitionDesignator partition_designator; MountOptions *o = NULL; const char *p; r = extract_first_word(&val, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE); if (r < 0) return r; if (r == 0) break; p = tuple; r = extract_many_words(&p, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &opts); if (r < 0) return r; if (r == 0) continue; if (r == 1) { o = new(MountOptions, 1); if (!o) return log_oom_debug(); *o = (MountOptions) { .partition_designator = PARTITION_ROOT, .options = TAKE_PTR(partition), }; LIST_APPEND(mount_options, options, o); continue; } partition_designator = partition_designator_from_string(partition); if (partition_designator < 0) continue; o = new(MountOptions, 1); if (!o) return log_oom_debug(); *o = (MountOptions) { .partition_designator = partition_designator, .options = TAKE_PTR(opts), }; LIST_APPEND(mount_options, options, o); } r = mount_image_add(&c->extension_images, &c->n_extension_images, &(MountImage) { .source = s, .mount_options = options, .ignore_enoent = permissive, .type = MOUNT_IMAGE_EXTENSION, }); if (r < 0) return log_oom_debug(); } else if ((val = startswith(l, "exec-context-extension-directories="))) { r = deserialize_strv(val, &c->extension_directories); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-set-credentials="))) { _cleanup_(exec_set_credential_freep) ExecSetCredential *sc = NULL; _cleanup_free_ char *id = NULL, *encrypted = NULL, *data = NULL; r = extract_many_words(&val, " ", EXTRACT_DONT_COALESCE_SEPARATORS, &id, &data, &encrypted); if (r < 0) return r; if (r != 3) return -EINVAL; r = parse_boolean(encrypted); if (r < 0) return r; sc = new(ExecSetCredential, 1); if (!sc) return -ENOMEM; *sc = (ExecSetCredential) { .id = TAKE_PTR(id), .encrypted = r, }; r = unbase64mem(data, &sc->data, &sc->size); if (r < 0) return r; r = hashmap_ensure_put(&c->set_credentials, &exec_set_credential_hash_ops, sc->id, sc); if (r < 0) return r; TAKE_PTR(sc); } else if ((val = startswith(l, "exec-context-load-credentials="))) { _cleanup_(exec_load_credential_freep) ExecLoadCredential *lc = NULL; _cleanup_free_ char *id = NULL, *encrypted = NULL, *path = NULL; r = extract_many_words(&val, " ", EXTRACT_DONT_COALESCE_SEPARATORS, &id, &path, &encrypted); if (r < 0) return r; if (r != 3) return -EINVAL; r = parse_boolean(encrypted); if (r < 0) return r; lc = new(ExecLoadCredential, 1); if (!lc) return -ENOMEM; *lc = (ExecLoadCredential) { .id = TAKE_PTR(id), .path = TAKE_PTR(path), .encrypted = r, }; r = hashmap_ensure_put(&c->load_credentials, &exec_load_credential_hash_ops, lc->id, lc); if (r < 0) return r; TAKE_PTR(lc); } else if ((val = startswith(l, "exec-context-import-credentials="))) { r = set_ensure_allocated(&c->import_credentials, &string_hash_ops); if (r < 0) return r; r = set_put_strdup(&c->import_credentials, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-root-image-policy="))) { if (c->root_image_policy) return -EINVAL; /* duplicated */ r = image_policy_from_string(val, &c->root_image_policy); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-mount-image-policy="))) { if (c->mount_image_policy) return -EINVAL; /* duplicated */ r = image_policy_from_string(val, &c->mount_image_policy); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-extension-image-policy="))) { if (c->extension_image_policy) return -EINVAL; /* duplicated */ r = image_policy_from_string(val, &c->extension_image_policy); if (r < 0) return r; } else log_warning("Failed to parse serialized line, ignoring: %s", l); } return 0; } static int exec_command_serialize(const ExecCommand *c, FILE *f) { int r; assert(c); assert(f); r = serialize_item(f, "exec-command-path", c->path); if (r < 0) return r; r = serialize_strv(f, "exec-command-argv", c->argv); if (r < 0) return r; r = serialize_item_format(f, "exec-command-flags", "%d", (int) c->flags); if (r < 0) return r; fputc('\n', f); /* End marker */ return 0; } static int exec_command_deserialize(ExecCommand *c, FILE *f) { int r; assert(c); assert(f); for (;;) { _cleanup_free_ char *l = NULL; const char *val; r = deserialize_read_line(f, &l); if (r < 0) return r; if (r == 0) /* eof or end marker */ break; if ((val = startswith(l, "exec-command-path="))) { r = free_and_strdup(&c->path, val); if (r < 0) return r; } else if ((val = startswith(l, "exec-command-argv="))) { r = deserialize_strv(val, &c->argv); if (r < 0) return r; } else if ((val = startswith(l, "exec-command-flags="))) { r = safe_atoi(val, &c->flags); if (r < 0) return r; } else log_warning("Failed to parse serialized line, ignoring: %s", l); } return 0; } int exec_serialize_invocation( FILE *f, FDSet *fds, const ExecContext *ctx, const ExecCommand *cmd, const ExecParameters *p, const ExecRuntime *rt, const CGroupContext *cg) { int r; assert(f); assert(fds); r = exec_context_serialize(ctx, f); if (r < 0) return log_debug_errno(r, "Failed to serialize context: %m"); r = exec_command_serialize(cmd, f); if (r < 0) return log_debug_errno(r, "Failed to serialize command: %m"); r = exec_parameters_serialize(p, ctx, f, fds); if (r < 0) return log_debug_errno(r, "Failed to serialize parameters: %m"); r = exec_runtime_serialize(rt, f, fds); if (r < 0) return log_debug_errno(r, "Failed to serialize runtime: %m"); r = exec_cgroup_context_serialize(cg, f); if (r < 0) return log_debug_errno(r, "Failed to serialize cgroup context: %m"); return 0; } int exec_deserialize_invocation( FILE *f, FDSet *fds, ExecContext *ctx, ExecCommand *cmd, ExecParameters *p, ExecRuntime *rt, CGroupContext *cg) { int r; assert(f); assert(fds); r = exec_context_deserialize(ctx, f); if (r < 0) return log_debug_errno(r, "Failed to deserialize context: %m"); r = exec_command_deserialize(cmd, f); if (r < 0) return log_debug_errno(r, "Failed to deserialize command: %m"); r = exec_parameters_deserialize(p, f, fds); if (r < 0) return log_debug_errno(r, "Failed to deserialize parameters: %m"); r = exec_runtime_deserialize(rt, f, fds); if (r < 0) return log_debug_errno(r, "Failed to deserialize runtime: %m"); r = exec_cgroup_context_deserialize(cg, f); if (r < 0) return log_debug_errno(r, "Failed to deserialize cgroup context: %m"); return 0; }