From a70581ffb5c13c91c76ff73ba6f5f3ff59c5a915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?X=E2=84=B9=20Ruoyao?= Date: Tue, 16 Feb 2021 23:58:56 +0800 Subject: [PATCH] New directives PrivateIPC and IPCNamespacePath --- man/org.freedesktop.systemd1.xml | 48 ++++++ man/systemd.exec.xml | 49 ++++++- man/systemd.unit.xml | 12 +- src/core/dbus-execute.c | 8 + src/core/execute.c | 155 ++++++++++++++++++-- src/core/execute.h | 5 + src/core/load-fragment-gperf.gperf.m4 | 2 + src/core/socket.c | 8 + src/shared/bus-unit-util.c | 2 + src/test/test-stat-util.c | 2 +- test/fuzz/fuzz-unit-file/directives.service | 2 + 11 files changed, 270 insertions(+), 23 deletions(-) diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml index 21630478962..aff43217e16 100644 --- a/man/org.freedesktop.systemd1.xml +++ b/man/org.freedesktop.systemd1.xml @@ -2693,6 +2693,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly b PrivateMounts = ...; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b PrivateIPC = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s ProtectHome = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s ProtectSystem = '...'; @@ -2777,6 +2779,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s NetworkNamespacePath = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s IPCNamespacePath = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s KillMode = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly i KillSignal = ...; @@ -3194,6 +3198,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + + @@ -3278,6 +3284,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + + @@ -3772,6 +3780,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + + @@ -3856,6 +3866,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + + @@ -4454,6 +4466,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly b PrivateMounts = ...; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b PrivateIPC = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s ProtectHome = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s ProtectSystem = '...'; @@ -4538,6 +4552,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s NetworkNamespacePath = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s IPCNamespacePath = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s KillMode = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly i KillSignal = ...; @@ -4983,6 +4999,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { + + @@ -5067,6 +5085,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { + + @@ -5559,6 +5579,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { + + @@ -5643,6 +5665,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { + + @@ -6143,6 +6167,8 @@ node /org/freedesktop/systemd1/unit/home_2emount { @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly b PrivateMounts = ...; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b PrivateIPC = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s ProtectHome = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s ProtectSystem = '...'; @@ -6227,6 +6253,8 @@ node /org/freedesktop/systemd1/unit/home_2emount { @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s NetworkNamespacePath = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s IPCNamespacePath = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s KillMode = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly i KillSignal = ...; @@ -6600,6 +6628,8 @@ node /org/freedesktop/systemd1/unit/home_2emount { + + @@ -6684,6 +6714,8 @@ node /org/freedesktop/systemd1/unit/home_2emount { + + @@ -7094,6 +7126,8 @@ node /org/freedesktop/systemd1/unit/home_2emount { + + @@ -7178,6 +7212,8 @@ node /org/freedesktop/systemd1/unit/home_2emount { + + @@ -7799,6 +7835,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap { @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly b PrivateMounts = ...; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b PrivateIPC = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s ProtectHome = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s ProtectSystem = '...'; @@ -7883,6 +7921,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap { @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s NetworkNamespacePath = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s IPCNamespacePath = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s KillMode = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly i KillSignal = ...; @@ -8242,6 +8282,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap { + + @@ -8326,6 +8368,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap { + + @@ -8722,6 +8766,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap { + + @@ -8806,6 +8852,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap { + + diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 30e64224c3e..51f873f8cd9 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -1603,6 +1603,53 @@ BindReadOnlyPaths=/var/lib/systemd + + PrivateIPC= + + Takes a boolean argument. If true, sets up a new IPC namespace for the executed processes. + Each IPC namespace has its own set of System V IPC identifiers and its own POSIX message queue file system. + This is useful to avoid name clash of IPC identifiers. Defaults to false. It is possible to run two or + more units within the same private IPC namespace by using the JoinsNamespaceOf= directive, + see systemd.unit5 for + details. + + Note that IPC namespacing does not have an effect on + AF_UNIX sockets, which are the most common + form of IPC used on Linux. Instead, AF_UNIX + sockets in the file system are subject to mount namespacing, and + those in the abstract namespace are subject to network namespacing. + IPC namespacing only has an effect on SysV IPC (which is mostly + legacy) as well as POSIX message queues (for which + AF_UNIX/SOCK_SEQPACKET + sockets are typically a better replacement). IPC namespacing also + has no effect on POSIX shared memory (which is subject to mount + namespacing) either. See + ipc_namespaces7 for + the details. + + Note that the implementation of this setting might be impossible (for example if IPC namespaces are + not available), and the unit should be written in a way that does not solely rely on this setting for + security. + + + + + + IPCNamespacePath= + + Takes an absolute file system path refererring to a Linux IPC namespace + pseudo-file (i.e. a file like /proc/$PID/ns/ipc or a bind mount or symlink to + one). When set the invoked processes are added to the network namespace referenced by that path. The + path has to point to a valid namespace file at the moment the processes are forked off. If this + option is used PrivateIPC= has no effect. If this option is used together with + JoinsNamespaceOf= then it only has an effect if this unit is started before any of + the listed units that have PrivateIPC= or + IPCNamespacePath= configured, as otherwise the network namespace of those + units is reused. + + + + PrivateUsers= @@ -3585,7 +3632,7 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy 226 EXIT_NAMESPACE - Failed to set up mount namespacing. See ReadOnlyPaths= and related settings above. + Failed to set up mount, UTS, or IPC namespacing. See ReadOnlyPaths=, ProtectHostname=, PrivateIPC=, and related settings above. 227 diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index 6a9d4dc486d..20e52c56642 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -799,14 +799,16 @@ For units that start processes (such as service units), lists one or more other units whose network and/or temporary file namespace to join. This only applies to unit types which support - the PrivateNetwork=, NetworkNamespacePath= and + the PrivateNetwork=, NetworkNamespacePath=, + PrivateIPC=, IPCNamespacePath=, and PrivateTmp= directives (see systemd.exec5 for details). If a unit that has this setting set is started, its processes will see the same - /tmp/, /var/tmp/ and network namespace as one listed unit - that is started. If multiple listed units are already started, it is not defined which namespace is - joined. Note that this setting only has an effect if - PrivateNetwork=/NetworkNamespacePath= and/or + /tmp/, /var/tmp/, IPC namespace and network namespace as + one listed unit that is started. If multiple listed units are already started, it is not defined + which namespace is joined. Note that this setting only has an effect if + PrivateNetwork=/NetworkNamespacePath=, + PrivateIPC=/IPCNamespacePath= and/or PrivateTmp= is enabled for both the unit that joins the namespace and the unit whose namespace is joined. diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index a4817ca6de7..f474a02b0e2 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -1162,6 +1162,7 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("PrivateNetwork", "b", bus_property_get_bool, offsetof(ExecContext, private_network), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("PrivateUsers", "b", bus_property_get_bool, offsetof(ExecContext, private_users), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("PrivateMounts", "b", bus_property_get_bool, offsetof(ExecContext, private_mounts), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("PrivateIPC", "b", bus_property_get_bool, offsetof(ExecContext, private_ipc), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ProtectHome", "s", property_get_protect_home, offsetof(ExecContext, protect_home), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ProtectSystem", "s", property_get_protect_system, offsetof(ExecContext, protect_system), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SameProcessGroup", "b", bus_property_get_bool, offsetof(ExecContext, same_pgrp), SD_BUS_VTABLE_PROPERTY_CONST), @@ -1204,6 +1205,7 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("ProcSubset", "s", property_get_proc_subset, offsetof(ExecContext, proc_subset), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ProtectHostname", "b", bus_property_get_bool, offsetof(ExecContext, protect_hostname), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("NetworkNamespacePath", "s", NULL, offsetof(ExecContext, network_namespace_path), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("IPCNamespacePath", "s", NULL, offsetof(ExecContext, ipc_namespace_path), SD_BUS_VTABLE_PROPERTY_CONST), /* Obsolete/redundant properties: */ SD_BUS_PROPERTY("Capabilities", "s", property_get_empty_string, 0, SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), @@ -1753,6 +1755,9 @@ int bus_exec_context_set_transient_property( if (streq(name, "PrivateNetwork")) return bus_set_transient_bool(u, name, &c->private_network, message, flags, error); + if (streq(name, "PrivateIPC")) + return bus_set_transient_bool(u, name, &c->private_ipc, message, flags, error); + if (streq(name, "PrivateUsers")) return bus_set_transient_bool(u, name, &c->private_users, message, flags, error); @@ -1873,6 +1878,9 @@ int bus_exec_context_set_transient_property( if (streq(name, "NetworkNamespacePath")) return bus_set_transient_path(u, name, &c->network_namespace_path, message, flags, error); + if (streq(name, "IPCNamespacePath")) + return bus_set_transient_path(u, name, &c->ipc_namespace_path, message, flags, error); + if (streq(name, "SupplementaryGroups")) { _cleanup_strv_free_ char **l = NULL; char **p; diff --git a/src/core/execute.c b/src/core/execute.c index 0a2d2e11585..684b5a233e7 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -3476,8 +3476,10 @@ static int close_remaining_fds( n_dont_close += n_fds; } - if (runtime) + if (runtime) { append_socket_pair(dont_close, &n_dont_close, runtime->netns_storage_socket); + append_socket_pair(dont_close, &n_dont_close, runtime->ipcns_storage_socket); + } if (dcreds) { if (dcreds->user) @@ -3925,6 +3927,14 @@ static int exec_child( } } + if (context->ipc_namespace_path && runtime && runtime->ipcns_storage_socket[0] >= 0) { + r = open_shareable_ns_path(runtime->ipcns_storage_socket, context->ipc_namespace_path, CLONE_NEWIPC); + if (r < 0) { + *exit_status = EXIT_NAMESPACE; + return log_unit_error_errno(unit, r, "Failed to open IPC namespace path %s: %m", context->ipc_namespace_path); + } + } + r = setup_input(context, params, socket_fd, named_iofds); if (r < 0) { *exit_status = EXIT_STDIN; @@ -4211,6 +4221,25 @@ static int exec_child( log_unit_warning(unit, "PrivateNetwork=yes is configured, but the kernel does not support network namespaces, ignoring."); } + if ((context->private_ipc || context->ipc_namespace_path) && runtime && runtime->ipcns_storage_socket[0] >= 0) { + + if (ns_type_supported(NAMESPACE_IPC)) { + r = setup_shareable_ns(runtime->ipcns_storage_socket, CLONE_NEWIPC); + if (r == -EPERM) + log_unit_warning_errno(unit, r, + "PrivateIPC=yes is configured, but IPC namespace setup failed, ignoring: %m"); + else if (r < 0) { + *exit_status = EXIT_NAMESPACE; + return log_unit_error_errno(unit, r, "Failed to set up IPC namespacing: %m"); + } + } else if (context->ipc_namespace_path) { + *exit_status = EXIT_NAMESPACE; + return log_unit_error_errno(unit, SYNTHETIC_ERRNO(EOPNOTSUPP), + "IPCNamespacePath= is not supported, refusing."); + } else + log_unit_warning(unit, "PrivateIPC=yes is configured, but the kernel does not support IPC namespaces, ignoring."); + } + needs_mount_namespace = exec_needs_mount_namespace(context, params, runtime); if (needs_mount_namespace) { _cleanup_free_ char *error_path = NULL; @@ -4314,7 +4343,7 @@ static int exec_child( #endif /* We repeat the fd closing here, to make sure that nothing is leaked from the PAM modules. Note that we are - * more aggressive this time since socket_fd and the netns fds we don't need anymore. We do keep the exec_fd + * more aggressive this time since socket_fd and the netns and ipcns fds we don't need anymore. We do keep the exec_fd * however if we have it as we want to keep it open until the final execve(). */ r = close_all_fds(keep_fds, n_keep_fds); @@ -6057,6 +6086,7 @@ static ExecRuntime* exec_runtime_free(ExecRuntime *rt, bool destroy) { rt->tmp_dir = mfree(rt->tmp_dir); rt->var_tmp_dir = mfree(rt->var_tmp_dir); safe_close_pair(rt->netns_storage_socket); + safe_close_pair(rt->ipcns_storage_socket); return mfree(rt); } @@ -6081,6 +6111,7 @@ static int exec_runtime_allocate(ExecRuntime **ret, const char *id) { *n = (ExecRuntime) { .id = TAKE_PTR(id_copy), .netns_storage_socket = { -1, -1 }, + .ipcns_storage_socket = { -1, -1 }, }; *ret = n; @@ -6093,6 +6124,7 @@ static int exec_runtime_add( char **tmp_dir, char **var_tmp_dir, int netns_storage_socket[2], + int ipcns_storage_socket[2], ExecRuntime **ret) { _cleanup_(exec_runtime_freep) ExecRuntime *rt = NULL; @@ -6101,7 +6133,7 @@ static int exec_runtime_add( assert(m); assert(id); - /* tmp_dir, var_tmp_dir, netns_storage_socket fds are donated on success */ + /* tmp_dir, var_tmp_dir, {net,ipc}ns_storage_socket fds are donated on success */ r = exec_runtime_allocate(&rt, id); if (r < 0) @@ -6120,6 +6152,11 @@ static int exec_runtime_add( rt->netns_storage_socket[1] = TAKE_FD(netns_storage_socket[1]); } + if (ipcns_storage_socket) { + rt->ipcns_storage_socket[0] = TAKE_FD(ipcns_storage_socket[0]); + rt->ipcns_storage_socket[1] = TAKE_FD(ipcns_storage_socket[1]); + } + rt->manager = m; if (ret) @@ -6136,7 +6173,7 @@ static int exec_runtime_make( ExecRuntime **ret) { _cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL; - _cleanup_close_pair_ int netns_storage_socket[2] = { -1, -1 }; + _cleanup_close_pair_ int netns_storage_socket[2] = { -1, -1 }, ipcns_storage_socket[2] = { -1, -1 }; int r; assert(m); @@ -6144,7 +6181,7 @@ static int exec_runtime_make( assert(id); /* It is not necessary to create ExecRuntime object. */ - if (!c->private_network && !c->private_tmp && !c->network_namespace_path) { + if (!c->private_network && !c->private_ipc && !c->private_tmp && !c->network_namespace_path) { *ret = NULL; return 0; } @@ -6163,7 +6200,12 @@ static int exec_runtime_make( return -errno; } - r = exec_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_storage_socket, ret); + if (c->private_ipc || c->ipc_namespace_path) { + if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ipcns_storage_socket) < 0) + return -errno; + } + + r = exec_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_storage_socket, ipcns_storage_socket, ret); if (r < 0) return r; @@ -6254,6 +6296,26 @@ int exec_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) { fprintf(f, " netns-socket-1=%i", copy); } + if (rt->ipcns_storage_socket[0] >= 0) { + int copy; + + copy = fdset_put_dup(fds, rt->ipcns_storage_socket[0]); + if (copy < 0) + return copy; + + fprintf(f, " ipcns-socket-0=%i", copy); + } + + if (rt->ipcns_storage_socket[1] >= 0) { + int copy; + + copy = fdset_put_dup(fds, rt->ipcns_storage_socket[1]); + if (copy < 0) + return copy; + + fprintf(f, " ipcns-socket-1=%i", copy); + } + fputc('\n', f); } @@ -6335,6 +6397,28 @@ int exec_runtime_deserialize_compat(Unit *u, const char *key, const char *value, safe_close(rt->netns_storage_socket[1]); rt->netns_storage_socket[1] = fdset_remove(fds, fd); + + } else if (streq(key, "ipcns-socket-0")) { + int fd; + + if (safe_atoi(value, &fd) < 0 || !fdset_contains(fds, fd)) { + log_unit_debug(u, "Failed to parse ipcns socket value: %s", value); + return 0; + } + + safe_close(rt->ipcns_storage_socket[0]); + rt->ipcns_storage_socket[0] = fdset_remove(fds, fd); + + } else if (streq(key, "ipcns-socket-1")) { + int fd; + + if (safe_atoi(value, &fd) < 0 || !fdset_contains(fds, fd)) { + log_unit_debug(u, "Failed to parse ipcns socket value: %s", value); + return 0; + } + + safe_close(rt->ipcns_storage_socket[1]); + rt->ipcns_storage_socket[1] = fdset_remove(fds, fd); } else return 0; @@ -6358,7 +6442,7 @@ int exec_runtime_deserialize_compat(Unit *u, const char *key, const char *value, int exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) { _cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL; char *id = NULL; - int r, fdpair[] = {-1, -1}; + int r, netns_fdpair[] = {-1, -1}, ipcns_fdpair[] = {-1, -1}; const char *p, *v = value; size_t n; @@ -6401,13 +6485,13 @@ int exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) { n = strcspn(v, " "); buf = strndupa(v, n); - r = safe_atoi(buf, &fdpair[0]); + r = safe_atoi(buf, &netns_fdpair[0]); if (r < 0) return log_debug_errno(r, "Unable to parse exec-runtime specification netns-socket-0=%s: %m", buf); - if (!fdset_contains(fds, fdpair[0])) + if (!fdset_contains(fds, netns_fdpair[0])) return log_debug_errno(SYNTHETIC_ERRNO(EBADF), - "exec-runtime specification netns-socket-0= refers to unknown fd %d: %m", fdpair[0]); - fdpair[0] = fdset_remove(fds, fdpair[0]); + "exec-runtime specification netns-socket-0= refers to unknown fd %d: %m", netns_fdpair[0]); + netns_fdpair[0] = fdset_remove(fds, netns_fdpair[0]); if (v[n] != ' ') goto finalize; p = v + n + 1; @@ -6419,17 +6503,56 @@ int exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) { n = strcspn(v, " "); buf = strndupa(v, n); - r = safe_atoi(buf, &fdpair[1]); + + r = safe_atoi(buf, &netns_fdpair[1]); if (r < 0) return log_debug_errno(r, "Unable to parse exec-runtime specification netns-socket-1=%s: %m", buf); - if (!fdset_contains(fds, fdpair[1])) + if (!fdset_contains(fds, netns_fdpair[1])) return log_debug_errno(SYNTHETIC_ERRNO(EBADF), - "exec-runtime specification netns-socket-1= refers to unknown fd %d: %m", fdpair[1]); - fdpair[1] = fdset_remove(fds, fdpair[1]); + "exec-runtime specification netns-socket-1= refers to unknown fd %d: %m", netns_fdpair[1]); + netns_fdpair[1] = fdset_remove(fds, netns_fdpair[1]); + if (v[n] != ' ') + goto finalize; + p = v + n + 1; + } + + v = startswith(p, "ipcns-socket-0="); + if (v) { + char *buf; + + n = strcspn(v, " "); + buf = strndupa(v, n); + + r = safe_atoi(buf, &ipcns_fdpair[0]); + if (r < 0) + return log_debug_errno(r, "Unable to parse exec-runtime specification ipcns-socket-0=%s: %m", buf); + if (!fdset_contains(fds, ipcns_fdpair[0])) + return log_debug_errno(SYNTHETIC_ERRNO(EBADF), + "exec-runtime specification ipcns-socket-0= refers to unknown fd %d: %m", ipcns_fdpair[0]); + ipcns_fdpair[0] = fdset_remove(fds, ipcns_fdpair[0]); + if (v[n] != ' ') + goto finalize; + p = v + n + 1; + } + + v = startswith(p, "ipcns-socket-1="); + if (v) { + char *buf; + + n = strcspn(v, " "); + buf = strndupa(v, n); + + r = safe_atoi(buf, &ipcns_fdpair[1]); + if (r < 0) + return log_debug_errno(r, "Unable to parse exec-runtime specification ipcns-socket-1=%s: %m", buf); + if (!fdset_contains(fds, ipcns_fdpair[1])) + return log_debug_errno(SYNTHETIC_ERRNO(EBADF), + "exec-runtime specification ipcns-socket-1= refers to unknown fd %d: %m", ipcns_fdpair[1]); + ipcns_fdpair[1] = fdset_remove(fds, ipcns_fdpair[1]); } finalize: - r = exec_runtime_add(m, id, &tmp_dir, &var_tmp_dir, fdpair, NULL); + r = exec_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_fdpair, ipcns_fdpair, NULL); if (r < 0) return log_debug_errno(r, "Failed to add exec-runtime: %m"); return 0; diff --git a/src/core/execute.h b/src/core/execute.h index 20e1799b46a..4c7a5b874f3 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -117,6 +117,9 @@ struct ExecRuntime { /* An AF_UNIX socket pair, that contains a datagram containing a file descriptor referring to the network * namespace. */ int netns_storage_socket[2]; + + /* Like netns_storage_socket, but the file descriptor is referring to the IPC namespace. */ + int ipcns_storage_socket[2]; }; typedef enum ExecDirectoryType { @@ -280,6 +283,7 @@ struct ExecContext { bool private_devices; bool private_users; bool private_mounts; + bool private_ipc; bool protect_kernel_tunables; bool protect_kernel_modules; bool protect_kernel_logs; @@ -314,6 +318,7 @@ struct ExecContext { Set *address_families; char *network_namespace_path; + char *ipc_namespace_path; ExecDirectory directories[_EXEC_DIRECTORY_TYPE_MAX]; ExecPreserveMode runtime_directory_preserve_mode; diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 6a11ef0d9d7..21bbcffe41a 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -133,10 +133,12 @@ $1.ProtectKernelLogs, config_parse_bool, $1.ProtectClock, config_parse_bool, 0, offsetof($1, exec_context.protect_clock) $1.ProtectControlGroups, config_parse_bool, 0, offsetof($1, exec_context.protect_control_groups) $1.NetworkNamespacePath, config_parse_unit_path_printf, 0, offsetof($1, exec_context.network_namespace_path) +$1.IPCNamespacePath, config_parse_unit_path_printf, 0, offsetof($1, exec_context.ipc_namespace_path) $1.LogNamespace, config_parse_log_namespace, 0, offsetof($1, exec_context) $1.PrivateNetwork, config_parse_bool, 0, offsetof($1, exec_context.private_network) $1.PrivateUsers, config_parse_bool, 0, offsetof($1, exec_context.private_users) $1.PrivateMounts, config_parse_bool, 0, offsetof($1, exec_context.private_mounts) +$1.PrivateIPC, config_parse_bool, 0, offsetof($1, exec_context.private_ipc) $1.ProtectSystem, config_parse_protect_system, 0, offsetof($1, exec_context.protect_system) $1.ProtectHome, config_parse_protect_home, 0, offsetof($1, exec_context.protect_home) $1.MountFlags, config_parse_exec_mount_flags, 0, offsetof($1, exec_context.mount_flags) diff --git a/src/core/socket.c b/src/core/socket.c index 4bc77f3018a..6255e704ced 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -1552,6 +1552,14 @@ static int socket_address_listen_in_cgroup( return log_unit_error_errno(UNIT(s), r, "Failed to open network namespace path %s: %m", s->exec_context.network_namespace_path); } + if (s->exec_context.ipc_namespace_path && + s->exec_runtime && + s->exec_runtime->ipcns_storage_socket[0] >= 0) { + r = open_shareable_ns_path(s->exec_runtime->netns_storage_socket, s->exec_context.network_namespace_path, CLONE_NEWIPC); + if (r < 0) + return log_unit_error_errno(UNIT(s), r, "Failed to open IPC namespace path %s: %m", s->exec_context.ipc_namespace_path); + } + if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pair) < 0) return log_unit_error_errno(UNIT(s), errno, "Failed to create communication channel: %m"); diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index a58495dbf84..9ed53787915 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -882,6 +882,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con "ProtectProc", "ProcSubset", "NetworkNamespacePath", + "IPCNamespacePath", "LogNamespace")) return bus_append_string(m, field, eq); @@ -894,6 +895,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con "PrivateNetwork", "PrivateUsers", "PrivateMounts", + "PrivateIPC", "NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute", diff --git a/src/test/test-stat-util.c b/src/test/test-stat-util.c index 7f03db5fa7a..4dd18be65cd 100644 --- a/src/test/test-stat-util.c +++ b/src/test/test-stat-util.c @@ -2,8 +2,8 @@ #include #include -#include #include +#include #include "alloc-util.h" #include "fd-util.h" diff --git a/test/fuzz/fuzz-unit-file/directives.service b/test/fuzz/fuzz-unit-file/directives.service index bdc4515d626..55204463c8d 100644 --- a/test/fuzz/fuzz-unit-file/directives.service +++ b/test/fuzz/fuzz-unit-file/directives.service @@ -111,6 +111,7 @@ IOWriteIOPSMax= IPAccounting= IPAddressAllow= IPAddressDeny= +IPCNamespacePath= IPTOS= IPTTL= IgnoreOnIsolate= @@ -857,6 +858,7 @@ PivotRoot= Port= PowerKeyIgnoreInhibited= Private= +PrivateIPC= PrivateDevices= PrivateNetwork= PrivateTmp=