mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-02-28 17:57:25 +03:00
Merge pull request #3865 from poettering/remove-ipc
add RemoveIPC= service file setting
This commit is contained in:
commit
57e3375f83
@ -160,14 +160,14 @@
|
||||
use. However, UID/GIDs are recycled after a unit is terminated. Care should be taken that any processes running
|
||||
as part of a unit for which dynamic users/groups are enabled do not leave files or directories owned by these
|
||||
users/groups around, as a different unit might get the same UID/GID assigned later on, and thus gain access to
|
||||
these files or directories. If <varname>DynamicUser=</varname> is enabled, <varname>PrivateTmp=</varname> is
|
||||
implied. This ensures that the lifetime of temporary files created by the executed processes is bound to the
|
||||
runtime of the service, and hence the lifetime of the dynamic user/group. Since <filename>/tmp</filename> and
|
||||
<filename>/var/tmp</filename> are usually the only world-writable directories on a system this ensures that a
|
||||
unit making use of dynamic user/group allocation cannot leave files around after unit termination. Use
|
||||
<varname>RuntimeDirectory=</varname> (see below) in order to assign a writable runtime directory to a service,
|
||||
owned by the dynamic user/group and removed automatically when the unit is terminated. Defaults to
|
||||
off.</para></listitem>
|
||||
these files or directories. If <varname>DynamicUser=</varname> is enabled, <varname>RemoveIPC=</varname> and
|
||||
<varname>PrivateTmp=</varname> are implied. This ensures that the lifetime of IPC objects and temporary files
|
||||
created by the executed processes is bound to the runtime of the service, and hence the lifetime of the dynamic
|
||||
user/group. Since <filename>/tmp</filename> and <filename>/var/tmp</filename> are usually the only
|
||||
world-writable directories on a system this ensures that a unit making use of dynamic user/group allocation
|
||||
cannot leave files around after unit termination. Use <varname>RuntimeDirectory=</varname> (see below) in order
|
||||
to assign a writable runtime directory to a service, owned by the dynamic user/group and removed automatically
|
||||
when the unit is terminated. Defaults to off.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
@ -185,6 +185,18 @@
|
||||
user. This does not affect commands prefixed with <literal>+</literal>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>RemoveIPC=</varname></term>
|
||||
|
||||
<listitem><para>Takes a boolean parameter. If set, all System V and POSIX IPC objects owned by the user and
|
||||
group the processes of this unit are run as are removed when the unit is stopped. This setting only has an
|
||||
effect if at least one of <varname>User=</varname>, <varname>Group=</varname> and
|
||||
<varname>DynamicUser=</varname> are used. It has no effect on IPC objects owned by the root user. Specifically,
|
||||
this removes System V semaphores, as well as System V and POSIX shared memory segments and message queues. If
|
||||
multiple units use the same user or group the IPC objects are removed when the last of these units is
|
||||
stopped. This setting is implied if <varname>DynamicUser=</varname> is set.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>Nice=</varname></term>
|
||||
|
||||
@ -920,27 +932,19 @@
|
||||
<varlistentry>
|
||||
<term><varname>PrivateTmp=</varname></term>
|
||||
|
||||
<listitem><para>Takes a boolean argument. If true, sets up a
|
||||
new file system namespace for the executed processes and
|
||||
mounts private <filename>/tmp</filename> and
|
||||
<filename>/var/tmp</filename> directories inside it that is
|
||||
not shared by processes outside of the namespace. This is
|
||||
useful to secure access to temporary files of the process, but
|
||||
makes sharing between processes via <filename>/tmp</filename>
|
||||
or <filename>/var/tmp</filename> impossible. If this is
|
||||
enabled, all temporary files created by a service in these
|
||||
directories will be removed after the service is stopped.
|
||||
Defaults to false. It is possible to run two or more units
|
||||
within the same private <filename>/tmp</filename> and
|
||||
<filename>/var/tmp</filename> namespace by using the
|
||||
<listitem><para>Takes a boolean argument. If true, sets up a new file system namespace for the executed
|
||||
processes and mounts private <filename>/tmp</filename> and <filename>/var/tmp</filename> directories inside it
|
||||
that is not shared by processes outside of the namespace. This is useful to secure access to temporary files of
|
||||
the process, but makes sharing between processes via <filename>/tmp</filename> or <filename>/var/tmp</filename>
|
||||
impossible. If this is enabled, all temporary files created by a service in these directories will be removed
|
||||
after the service is stopped. Defaults to false. It is possible to run two or more units within the same
|
||||
private <filename>/tmp</filename> and <filename>/var/tmp</filename> namespace by using the
|
||||
<varname>JoinsNamespaceOf=</varname> directive, see
|
||||
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
for details. Note that using this setting will disconnect
|
||||
propagation of mounts from the service to the host
|
||||
(propagation in the opposite direction continues to work).
|
||||
This means that this setting may not be used for services
|
||||
which shall be able to install mount points in the main mount
|
||||
namespace.</para></listitem>
|
||||
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
|
||||
details. Note that using this setting will disconnect propagation of mounts from the service to the host
|
||||
(propagation in the opposite direction continues to work). This means that this setting may not be used for
|
||||
services which shall be able to install mount points in the main mount namespace. This setting is implied if
|
||||
<varname>DynamicUser=</varname> is set.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
|
@ -695,6 +695,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
|
||||
SD_BUS_PROPERTY("User", "s", NULL, offsetof(ExecContext, user), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Group", "s", NULL, offsetof(ExecContext, group), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("DynamicUser", "b", bus_property_get_bool, offsetof(ExecContext, dynamic_user), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RemoveIPC", "b", bus_property_get_bool, offsetof(ExecContext, remove_ipc), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("SupplementaryGroups", "as", NULL, offsetof(ExecContext, supplementary_groups), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("PAMName", "s", NULL, offsetof(ExecContext, pam_name), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ReadWriteDirectories", "as", NULL, offsetof(ExecContext, read_write_paths), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
|
||||
@ -1071,7 +1072,7 @@ int bus_exec_context_set_transient_property(
|
||||
"IgnoreSIGPIPE", "TTYVHangup", "TTYReset",
|
||||
"PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
|
||||
"NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute",
|
||||
"RestrictRealtime", "DynamicUser")) {
|
||||
"RestrictRealtime", "DynamicUser", "RemoveIPC")) {
|
||||
int b;
|
||||
|
||||
r = sd_bus_message_read(message, "b", &b);
|
||||
@ -1103,6 +1104,8 @@ int bus_exec_context_set_transient_property(
|
||||
c->restrict_realtime = b;
|
||||
else if (streq(name, "DynamicUser"))
|
||||
c->dynamic_user = b;
|
||||
else if (streq(name, "RemoveIPC"))
|
||||
c->remove_ipc = b;
|
||||
|
||||
unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, yes_no(b));
|
||||
}
|
||||
|
@ -117,6 +117,8 @@ const sd_bus_vtable bus_mount_vtable[] = {
|
||||
SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Mount, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("SloppyOptions", "b", bus_property_get_bool, offsetof(Mount, sloppy_options), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Mount, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("UID", "u", NULL, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("GID", "u", NULL, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
BUS_EXEC_COMMAND_VTABLE("ExecMount", offsetof(Mount, exec_command[MOUNT_EXEC_MOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
|
||||
BUS_EXEC_COMMAND_VTABLE("ExecUnmount", offsetof(Mount, exec_command[MOUNT_EXEC_UNMOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
|
||||
BUS_EXEC_COMMAND_VTABLE("ExecRemount", offsetof(Mount, exec_command[MOUNT_EXEC_REMOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
|
||||
|
@ -50,11 +50,6 @@ const sd_bus_vtable bus_service_vtable[] = {
|
||||
SD_BUS_PROPERTY("RuntimeMaxUSec", "t", bus_property_get_usec, offsetof(Service, runtime_max_usec), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("WatchdogUSec", "t", bus_property_get_usec, offsetof(Service, watchdog_usec), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
BUS_PROPERTY_DUAL_TIMESTAMP("WatchdogTimestamp", offsetof(Service, watchdog_timestamp), 0),
|
||||
/* The following four are obsolete, and thus marked hidden here. They moved into the Unit interface */
|
||||
SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
|
||||
SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
|
||||
SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
|
||||
SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
|
||||
SD_BUS_PROPERTY("FailureAction", "s", property_get_failure_action, offsetof(Service, failure_action), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("PermissionsStartOnly", "b", bus_property_get_bool, offsetof(Service, permissions_start_only), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RootDirectoryStartOnly", "b", bus_property_get_bool, offsetof(Service, root_directory_start_only), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
@ -70,6 +65,9 @@ const sd_bus_vtable bus_service_vtable[] = {
|
||||
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Service, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("USBFunctionDescriptors", "s", NULL, offsetof(Service, usb_function_descriptors), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("USBFunctionStrings", "s", NULL, offsetof(Service, usb_function_strings), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("UID", "u", NULL, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("GID", "u", NULL, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
|
||||
BUS_EXEC_STATUS_VTABLE("ExecMain", offsetof(Service, main_exec_status), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Service, exec_command[SERVICE_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
|
||||
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStart", offsetof(Service, exec_command[SERVICE_EXEC_START]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
|
||||
@ -77,6 +75,12 @@ const sd_bus_vtable bus_service_vtable[] = {
|
||||
BUS_EXEC_COMMAND_LIST_VTABLE("ExecReload", offsetof(Service, exec_command[SERVICE_EXEC_RELOAD]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
|
||||
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStop", offsetof(Service, exec_command[SERVICE_EXEC_STOP]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
|
||||
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPost", offsetof(Service, exec_command[SERVICE_EXEC_STOP_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
|
||||
|
||||
/* The following four are obsolete, and thus marked hidden here. They moved into the Unit interface */
|
||||
SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
|
||||
SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
|
||||
SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
|
||||
SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
|
||||
SD_BUS_VTABLE_END
|
||||
};
|
||||
|
||||
|
@ -152,6 +152,8 @@ const sd_bus_vtable bus_socket_vtable[] = {
|
||||
SD_BUS_PROPERTY("SocketProtocol", "i", bus_property_get_int, offsetof(Socket, socket_protocol), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("TriggerLimitIntervalUSec", "t", bus_property_get_usec, offsetof(Socket, trigger_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("TriggerLimitBurst", "u", bus_property_get_unsigned, offsetof(Socket, trigger_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("UID", "u", NULL, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("GID", "u", NULL, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Socket, exec_command[SOCKET_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
|
||||
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPost", offsetof(Socket, exec_command[SOCKET_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
|
||||
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPre", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
|
||||
|
@ -84,6 +84,8 @@ const sd_bus_vtable bus_swap_vtable[] = {
|
||||
SD_BUS_PROPERTY("TimeoutUSec", "t", bus_property_get_usec, offsetof(Swap, timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Swap, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Swap, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("UID", "u", NULL, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("GID", "u", NULL, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
BUS_EXEC_COMMAND_VTABLE("ExecActivate", offsetof(Swap, exec_command[SWAP_EXEC_ACTIVATE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
|
||||
BUS_EXEC_COMMAND_VTABLE("ExecDeactivate", offsetof(Swap, exec_command[SWAP_EXEC_DEACTIVATE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
|
||||
SD_BUS_VTABLE_END
|
||||
|
@ -660,10 +660,6 @@ const sd_bus_vtable bus_unit_vtable[] = {
|
||||
SD_BUS_PROPERTY("PropagatesReloadTo", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ReloadPropagatedFrom", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("JoinsNamespaceOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_JOINS_NAMESPACE_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
|
||||
SD_BUS_PROPERTY("RequisiteOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
|
||||
SD_BUS_PROPERTY("RequiredByOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
|
||||
SD_BUS_PROPERTY("RequisiteOfOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
|
||||
SD_BUS_PROPERTY("RequiresMountsFor", "as", NULL, offsetof(Unit, requires_mounts_for), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Documentation", "as", NULL, offsetof(Unit, documentation), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Description", "s", property_get_description, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
@ -705,7 +701,6 @@ const sd_bus_vtable bus_unit_vtable[] = {
|
||||
SD_BUS_PROPERTY("LoadError", "(ss)", property_get_load_error, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Transient", "b", bus_property_get_bool, offsetof(Unit, transient), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("StartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), /* obsolete alias name */
|
||||
SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
@ -721,6 +716,12 @@ const sd_bus_vtable bus_unit_vtable[] = {
|
||||
SD_BUS_METHOD("ResetFailed", NULL, NULL, bus_unit_method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("SetProperties", "ba(sv)", NULL, bus_unit_method_set_properties, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
|
||||
/* Obsolete properties or obsolete alias names */
|
||||
SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
|
||||
SD_BUS_PROPERTY("RequisiteOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
|
||||
SD_BUS_PROPERTY("RequiredByOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
|
||||
SD_BUS_PROPERTY("RequisiteOfOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
|
||||
SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
|
||||
SD_BUS_VTABLE_END
|
||||
};
|
||||
|
||||
|
@ -150,6 +150,42 @@ int dynamic_user_acquire(Manager *m, const char *name, DynamicUser** ret) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int make_uid_symlinks(uid_t uid, const char *name, bool b) {
|
||||
|
||||
char path1[strlen("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1];
|
||||
const char *path2;
|
||||
int r = 0;
|
||||
|
||||
/* Add direct additional symlinks for direct lookups of dynamic UIDs and their names by userspace code. The
|
||||
* only reason we have this is because dbus-daemon cannot use D-Bus for resolving users and groups (since it
|
||||
* would be its own client then). We hence keep these world-readable symlinks in place, so that the
|
||||
* unprivileged dbus user can read the mappings when it needs them via these symlinks instead of having to go
|
||||
* via the bus. Ideally, we'd use the lock files we keep for this anyway, but we can't since we use BSD locks
|
||||
* on them and as those may be taken by any user with read access we can't make them world-readable. */
|
||||
|
||||
xsprintf(path1, "/run/systemd/dynamic-uid/direct:" UID_FMT, uid);
|
||||
if (unlink(path1) < 0) {
|
||||
if (errno != ENOENT)
|
||||
r = -errno;
|
||||
}
|
||||
if (b) {
|
||||
if (symlink(name, path1) < 0)
|
||||
r = -errno;
|
||||
}
|
||||
|
||||
path2 = strjoina("/run/systemd/dynamic-uid/direct:", name);
|
||||
if (unlink(path2) < 0) {
|
||||
if (errno != ENOENT)
|
||||
r = -errno;
|
||||
}
|
||||
if (b) {
|
||||
if (symlink(path1 + strlen("/run/systemd/dynamic-uid/direct:"), path2) < 0)
|
||||
r = -errno;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int pick_uid(const char *name, uid_t *ret_uid) {
|
||||
|
||||
static const uint8_t hash_key[] = {
|
||||
@ -223,6 +259,7 @@ static int pick_uid(const char *name, uid_t *ret_uid) {
|
||||
}
|
||||
|
||||
(void) ftruncate(lock_fd, l);
|
||||
(void) make_uid_symlinks(candidate, name, true); /* also add direct lookup symlinks */
|
||||
|
||||
*ret_uid = candidate;
|
||||
r = lock_fd;
|
||||
@ -324,14 +361,16 @@ static int dynamic_user_push(DynamicUser *d, uid_t uid, int lock_fd) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void unlink_uid_lock(int lock_fd, uid_t uid) {
|
||||
static void unlink_uid_lock(int lock_fd, uid_t uid, const char *name) {
|
||||
char lock_path[strlen("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1];
|
||||
|
||||
if (lock_fd < 0)
|
||||
return;
|
||||
|
||||
xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, uid);
|
||||
(void) unlink_noerrno(lock_path);
|
||||
(void) unlink(lock_path);
|
||||
|
||||
(void) make_uid_symlinks(uid, name, false); /* remove direct lookup symlinks */
|
||||
}
|
||||
|
||||
int dynamic_user_realize(DynamicUser *d, uid_t *ret) {
|
||||
@ -399,7 +438,7 @@ int dynamic_user_realize(DynamicUser *d, uid_t *ret) {
|
||||
|
||||
/* So, we found a working UID/lock combination. Let's see if we actually still need it. */
|
||||
if (lockf(d->storage_socket[0], F_LOCK, 0) < 0) {
|
||||
unlink_uid_lock(uid_lock_fd, uid);
|
||||
unlink_uid_lock(uid_lock_fd, uid, d->name);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
@ -407,7 +446,7 @@ int dynamic_user_realize(DynamicUser *d, uid_t *ret) {
|
||||
if (r < 0) {
|
||||
if (r != -EAGAIN) {
|
||||
/* OK, something bad happened, let's get rid of the bits we acquired. */
|
||||
unlink_uid_lock(uid_lock_fd, uid);
|
||||
unlink_uid_lock(uid_lock_fd, uid, d->name);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
@ -416,7 +455,7 @@ int dynamic_user_realize(DynamicUser *d, uid_t *ret) {
|
||||
/* Hmm, so as it appears there's now something stored in the storage socket. Throw away what we
|
||||
* acquired, and use what's stored now. */
|
||||
|
||||
unlink_uid_lock(uid_lock_fd, uid);
|
||||
unlink_uid_lock(uid_lock_fd, uid, d->name);
|
||||
safe_close(uid_lock_fd);
|
||||
|
||||
uid = new_uid;
|
||||
@ -513,7 +552,7 @@ static int dynamic_user_close(DynamicUser *d) {
|
||||
goto finish;
|
||||
|
||||
/* This dynamic user was realized and dynamically allocated. In this case, let's remove the lock file. */
|
||||
unlink_uid_lock(lock_fd, uid);
|
||||
unlink_uid_lock(lock_fd, uid, d->name);
|
||||
r = 1;
|
||||
|
||||
finish:
|
||||
|
@ -91,6 +91,7 @@
|
||||
#include "selinux-util.h"
|
||||
#include "signal-util.h"
|
||||
#include "smack-util.h"
|
||||
#include "special.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
@ -1384,6 +1385,7 @@ static void do_idle_pipe_dance(int idle_pipe[4]) {
|
||||
}
|
||||
|
||||
static int build_environment(
|
||||
Unit *u,
|
||||
const ExecContext *c,
|
||||
const ExecParameters *p,
|
||||
unsigned n_fds,
|
||||
@ -1401,7 +1403,7 @@ static int build_environment(
|
||||
assert(c);
|
||||
assert(ret);
|
||||
|
||||
our_env = new0(char*, 12);
|
||||
our_env = new0(char*, 13);
|
||||
if (!our_env)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -1436,6 +1438,16 @@ static int build_environment(
|
||||
our_env[n_env++] = x;
|
||||
}
|
||||
|
||||
/* If this is D-Bus, tell the nss-systemd module, since it relies on being able to use D-Bus look up dynamic
|
||||
* users via PID 1, possibly dead-locking the dbus daemon. This way it will not use D-Bus to resolve names, but
|
||||
* check the database directly. */
|
||||
if (unit_has_name(u, SPECIAL_DBUS_SERVICE)) {
|
||||
x = strdup("SYSTEMD_NSS_BYPASS_BUS=1");
|
||||
if (!x)
|
||||
return -ENOMEM;
|
||||
our_env[n_env++] = x;
|
||||
}
|
||||
|
||||
if (home) {
|
||||
x = strappend("HOME=", home);
|
||||
if (!x)
|
||||
@ -1723,11 +1735,12 @@ static int close_remaining_fds(
|
||||
const ExecParameters *params,
|
||||
ExecRuntime *runtime,
|
||||
DynamicCreds *dcreds,
|
||||
int user_lookup_fd,
|
||||
int socket_fd,
|
||||
int *fds, unsigned n_fds) {
|
||||
|
||||
unsigned n_dont_close = 0;
|
||||
int dont_close[n_fds + 11];
|
||||
int dont_close[n_fds + 12];
|
||||
|
||||
assert(params);
|
||||
|
||||
@ -1755,9 +1768,40 @@ static int close_remaining_fds(
|
||||
append_socket_pair(dont_close, &n_dont_close, dcreds->group->storage_socket);
|
||||
}
|
||||
|
||||
if (user_lookup_fd >= 0)
|
||||
dont_close[n_dont_close++] = user_lookup_fd;
|
||||
|
||||
return close_all_fds(dont_close, n_dont_close);
|
||||
}
|
||||
|
||||
static int send_user_lookup(
|
||||
Unit *unit,
|
||||
int user_lookup_fd,
|
||||
uid_t uid,
|
||||
gid_t gid) {
|
||||
|
||||
assert(unit);
|
||||
|
||||
/* Send the resolved UID/GID to PID 1 after we learnt it. We send a single datagram, containing the UID/GID
|
||||
* data as well as the unit name. Note that we suppress sending this if no user/group to resolve was
|
||||
* specified. */
|
||||
|
||||
if (user_lookup_fd < 0)
|
||||
return 0;
|
||||
|
||||
if (!uid_is_valid(uid) && !gid_is_valid(gid))
|
||||
return 0;
|
||||
|
||||
if (writev(user_lookup_fd,
|
||||
(struct iovec[]) {
|
||||
{ .iov_base = &uid, .iov_len = sizeof(uid) },
|
||||
{ .iov_base = &gid, .iov_len = sizeof(gid) },
|
||||
{ .iov_base = unit->id, .iov_len = strlen(unit->id) }}, 3) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exec_child(
|
||||
Unit *unit,
|
||||
ExecCommand *command,
|
||||
@ -1769,6 +1813,7 @@ static int exec_child(
|
||||
int socket_fd,
|
||||
int *fds, unsigned n_fds,
|
||||
char **files_env,
|
||||
int user_lookup_fd,
|
||||
int *exit_status) {
|
||||
|
||||
_cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **accum_env = NULL, **final_argv = NULL;
|
||||
@ -1815,7 +1860,7 @@ static int exec_child(
|
||||
|
||||
log_forget_fds();
|
||||
|
||||
r = close_remaining_fds(params, runtime, dcreds, socket_fd, fds, n_fds);
|
||||
r = close_remaining_fds(params, runtime, dcreds, user_lookup_fd, socket_fd, fds, n_fds);
|
||||
if (r < 0) {
|
||||
*exit_status = EXIT_FDS;
|
||||
return r;
|
||||
@ -1862,7 +1907,7 @@ static int exec_child(
|
||||
return r;
|
||||
}
|
||||
|
||||
if (uid == UID_INVALID || gid == GID_INVALID) {
|
||||
if (!uid_is_valid(uid) || !gid_is_valid(gid)) {
|
||||
*exit_status = EXIT_USER;
|
||||
return -ESRCH;
|
||||
}
|
||||
@ -1902,6 +1947,14 @@ static int exec_child(
|
||||
}
|
||||
}
|
||||
|
||||
r = send_user_lookup(unit, user_lookup_fd, uid, gid);
|
||||
if (r < 0) {
|
||||
*exit_status = EXIT_USER;
|
||||
return r;
|
||||
}
|
||||
|
||||
user_lookup_fd = safe_close(user_lookup_fd);
|
||||
|
||||
/* If a socket is connected to STDIN/STDOUT/STDERR, we
|
||||
* must sure to drop O_NONBLOCK */
|
||||
if (socket_fd >= 0)
|
||||
@ -2059,6 +2112,7 @@ static int exec_child(
|
||||
}
|
||||
|
||||
r = build_environment(
|
||||
unit,
|
||||
context,
|
||||
params,
|
||||
n_fds,
|
||||
@ -2501,6 +2555,7 @@ int exec_spawn(Unit *unit,
|
||||
socket_fd,
|
||||
fds, n_fds,
|
||||
files_env,
|
||||
unit->manager->user_lookup_fds[1],
|
||||
&exit_status);
|
||||
if (r < 0) {
|
||||
log_open();
|
||||
|
@ -178,6 +178,7 @@ struct ExecContext {
|
||||
bool no_new_privileges;
|
||||
|
||||
bool dynamic_user;
|
||||
bool remove_ipc;
|
||||
|
||||
/* This is not exposed to the user but available
|
||||
* internally. We need it to make sure that whenever we spawn
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include "bus-error.h"
|
||||
#include "bus-kernel.h"
|
||||
#include "bus-util.h"
|
||||
#include "clean-ipc.h"
|
||||
#include "dbus-job.h"
|
||||
#include "dbus-manager.h"
|
||||
#include "dbus-unit.h"
|
||||
@ -81,6 +82,7 @@
|
||||
#include "transaction.h"
|
||||
#include "umask-util.h"
|
||||
#include "unit-name.h"
|
||||
#include "user-util.h"
|
||||
#include "util.h"
|
||||
#include "virt.h"
|
||||
#include "watchdog.h"
|
||||
@ -98,6 +100,7 @@ static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, ui
|
||||
static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
|
||||
static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
|
||||
static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
|
||||
static int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
|
||||
static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata);
|
||||
static int manager_dispatch_run_queue(sd_event_source *source, void *userdata);
|
||||
static int manager_run_generators(Manager *m);
|
||||
@ -590,6 +593,8 @@ int manager_new(UnitFileScope scope, bool test_run, Manager **_m) {
|
||||
m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->cgroup_inotify_fd =
|
||||
m->ask_password_inotify_fd = -1;
|
||||
|
||||
m->user_lookup_fds[0] = m->user_lookup_fds[1] = -1;
|
||||
|
||||
m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
|
||||
|
||||
m->have_ask_password = -EINVAL; /* we don't know */
|
||||
@ -812,6 +817,59 @@ static int manager_setup_cgroups_agent(Manager *m) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int manager_setup_user_lookup_fd(Manager *m) {
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
/* Set up the socket pair used for passing UID/GID resolution results from forked off processes to PID
|
||||
* 1. Background: we can't do name lookups (NSS) from PID 1, since it might involve IPC and thus activation,
|
||||
* and we might hence deadlock on ourselves. Hence we do all user/group lookups asynchronously from the forked
|
||||
* off processes right before executing the binaries to start. In order to be able to clean up any IPC objects
|
||||
* created by a unit (see RemoveIPC=) we need to know in PID 1 the used UID/GID of the executed processes,
|
||||
* hence we establish this communication channel so that forked off processes can pass their UID/GID
|
||||
* information back to PID 1. The forked off processes send their resolved UID/GID to PID 1 in a simple
|
||||
* datagram, along with their unit name, so that we can share one communication socket pair among all units for
|
||||
* this purpose.
|
||||
*
|
||||
* You might wonder why we need a communication channel for this that is independent of the usual notification
|
||||
* socket scheme (i.e. $NOTIFY_SOCKET). The primary difference is about trust: data sent via the $NOTIFY_SOCKET
|
||||
* channel is only accepted if it originates from the right unit and if reception was enabled for it. The user
|
||||
* lookup socket OTOH is only accessible by PID 1 and its children until they exec(), and always available.
|
||||
*
|
||||
* Note that this function is called under two circumstances: when we first initialize (in which case we
|
||||
* allocate both the socket pair and the event source to listen on it), and when we deserialize after a reload
|
||||
* (in which case the socket pair already exists but we still need to allocate the event source for it). */
|
||||
|
||||
if (m->user_lookup_fds[0] < 0) {
|
||||
|
||||
/* Free all secondary fields */
|
||||
safe_close_pair(m->user_lookup_fds);
|
||||
m->user_lookup_event_source = sd_event_source_unref(m->user_lookup_event_source);
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, m->user_lookup_fds) < 0)
|
||||
return log_error_errno(errno, "Failed to allocate user lookup socket: %m");
|
||||
|
||||
(void) fd_inc_rcvbuf(m->user_lookup_fds[0], NOTIFY_RCVBUF_SIZE);
|
||||
}
|
||||
|
||||
if (!m->user_lookup_event_source) {
|
||||
r = sd_event_add_io(m->event, &m->user_lookup_event_source, m->user_lookup_fds[0], EPOLLIN, manager_dispatch_user_lookup_fd, m);
|
||||
if (r < 0)
|
||||
return log_error_errno(errno, "Failed to allocate user lookup event source: %m");
|
||||
|
||||
/* Process even earlier than the notify event source, so that we always know first about valid UID/GID
|
||||
* resolutions */
|
||||
r = sd_event_source_set_priority(m->user_lookup_event_source, SD_EVENT_PRIORITY_NORMAL-8);
|
||||
if (r < 0)
|
||||
return log_error_errno(errno, "Failed to set priority ot user lookup event source: %m");
|
||||
|
||||
(void) sd_event_source_set_description(m->user_lookup_event_source, "user-lookup");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int manager_connect_bus(Manager *m, bool reexecuting) {
|
||||
bool try_bus_connect;
|
||||
|
||||
@ -853,8 +911,7 @@ enum {
|
||||
_GC_OFFSET_MAX
|
||||
};
|
||||
|
||||
static void unit_gc_mark_good(Unit *u, unsigned gc_marker)
|
||||
{
|
||||
static void unit_gc_mark_good(Unit *u, unsigned gc_marker) {
|
||||
Iterator i;
|
||||
Unit *other;
|
||||
|
||||
@ -1021,12 +1078,14 @@ Manager* manager_free(Manager *m) {
|
||||
sd_event_source_unref(m->time_change_event_source);
|
||||
sd_event_source_unref(m->jobs_in_progress_event_source);
|
||||
sd_event_source_unref(m->run_queue_event_source);
|
||||
sd_event_source_unref(m->user_lookup_event_source);
|
||||
|
||||
safe_close(m->signal_fd);
|
||||
safe_close(m->notify_fd);
|
||||
safe_close(m->cgroups_agent_fd);
|
||||
safe_close(m->time_change_fd);
|
||||
safe_close(m->kdbus_fd);
|
||||
safe_close_pair(m->user_lookup_fds);
|
||||
|
||||
manager_close_ask_password(m);
|
||||
|
||||
@ -1052,6 +1111,9 @@ Manager* manager_free(Manager *m) {
|
||||
assert(hashmap_isempty(m->units_requiring_mounts_for));
|
||||
hashmap_free(m->units_requiring_mounts_for);
|
||||
|
||||
hashmap_free(m->uid_refs);
|
||||
hashmap_free(m->gid_refs);
|
||||
|
||||
free(m);
|
||||
return NULL;
|
||||
}
|
||||
@ -1221,6 +1283,10 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
|
||||
if (q < 0 && r == 0)
|
||||
r = q;
|
||||
|
||||
q = manager_setup_user_lookup_fd(m);
|
||||
if (q < 0 && r == 0)
|
||||
r = q;
|
||||
|
||||
/* We might have deserialized the kdbus control fd, but if we
|
||||
* didn't, then let's create the bus now. */
|
||||
manager_connect_bus(m, !!serialization);
|
||||
@ -1232,6 +1298,10 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
|
||||
/* Release any dynamic users no longer referenced */
|
||||
dynamic_user_vacuum(m, true);
|
||||
|
||||
/* Release any references to UIDs/GIDs no longer referenced, and destroy any IPC owned by them */
|
||||
manager_vacuum_uid_refs(m);
|
||||
manager_vacuum_gid_refs(m);
|
||||
|
||||
if (serialization) {
|
||||
assert(m->n_reloading > 0);
|
||||
m->n_reloading--;
|
||||
@ -2396,6 +2466,20 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
|
||||
fprintf(f, "cgroups-agent-fd=%i\n", copy);
|
||||
}
|
||||
|
||||
if (m->user_lookup_fds[0] >= 0) {
|
||||
int copy0, copy1;
|
||||
|
||||
copy0 = fdset_put_dup(fds, m->user_lookup_fds[0]);
|
||||
if (copy0 < 0)
|
||||
return copy0;
|
||||
|
||||
copy1 = fdset_put_dup(fds, m->user_lookup_fds[1]);
|
||||
if (copy1 < 0)
|
||||
return copy1;
|
||||
|
||||
fprintf(f, "user-lookup=%i %i\n", copy0, copy1);
|
||||
}
|
||||
|
||||
if (m->kdbus_fd >= 0) {
|
||||
int copy;
|
||||
|
||||
@ -2412,6 +2496,9 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
manager_serialize_uid_refs(m, f);
|
||||
manager_serialize_gid_refs(m, f);
|
||||
|
||||
fputc('\n', f);
|
||||
|
||||
HASHMAP_FOREACH_KEY(u, t, m->units, i) {
|
||||
@ -2578,6 +2665,18 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
|
||||
m->cgroups_agent_fd = fdset_remove(fds, fd);
|
||||
}
|
||||
|
||||
} else if (startswith(l, "user-lookup=")) {
|
||||
int fd0, fd1;
|
||||
|
||||
if (sscanf(l + 12, "%i %i", &fd0, &fd1) != 2 || fd0 < 0 || fd1 < 0 || fd0 == fd1 || !fdset_contains(fds, fd0) || !fdset_contains(fds, fd1))
|
||||
log_debug("Failed to parse user lookup fd: %s", l + 12);
|
||||
else {
|
||||
m->user_lookup_event_source = sd_event_source_unref(m->user_lookup_event_source);
|
||||
safe_close_pair(m->user_lookup_fds);
|
||||
m->user_lookup_fds[0] = fdset_remove(fds, fd0);
|
||||
m->user_lookup_fds[1] = fdset_remove(fds, fd1);
|
||||
}
|
||||
|
||||
} else if (startswith(l, "kdbus-fd=")) {
|
||||
int fd;
|
||||
|
||||
@ -2590,6 +2689,10 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
|
||||
|
||||
} else if (startswith(l, "dynamic-user="))
|
||||
dynamic_user_deserialize_one(m, l + 13, fds);
|
||||
else if (startswith(l, "destroy-ipc-uid="))
|
||||
manager_deserialize_uid_refs_one(m, l + 16);
|
||||
else if (startswith(l, "destroy-ipc-gid="))
|
||||
manager_deserialize_gid_refs_one(m, l + 16);
|
||||
else {
|
||||
int k;
|
||||
|
||||
@ -2672,6 +2775,8 @@ int manager_reload(Manager *m) {
|
||||
lookup_paths_flush_generator(&m->lookup_paths);
|
||||
lookup_paths_free(&m->lookup_paths);
|
||||
dynamic_user_vacuum(m, false);
|
||||
m->uid_refs = hashmap_free(m->uid_refs);
|
||||
m->gid_refs = hashmap_free(m->gid_refs);
|
||||
|
||||
q = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL);
|
||||
if (q < 0 && r >= 0)
|
||||
@ -2705,12 +2810,20 @@ int manager_reload(Manager *m) {
|
||||
if (q < 0 && r >= 0)
|
||||
r = q;
|
||||
|
||||
q = manager_setup_user_lookup_fd(m);
|
||||
if (q < 0 && r >= 0)
|
||||
r = q;
|
||||
|
||||
/* Third, fire things up! */
|
||||
manager_coldplug(m);
|
||||
|
||||
/* Release any dynamic users no longer referenced */
|
||||
dynamic_user_vacuum(m, true);
|
||||
|
||||
/* Release any references to UIDs/GIDs no longer referenced, and destroy any IPC owned by them */
|
||||
manager_vacuum_uid_refs(m);
|
||||
manager_vacuum_gid_refs(m);
|
||||
|
||||
/* Sync current state of bus names with our set of listening units */
|
||||
if (m->api_bus)
|
||||
manager_sync_bus_names(m, m->api_bus);
|
||||
@ -3144,6 +3257,300 @@ ManagerState manager_state(Manager *m) {
|
||||
return MANAGER_RUNNING;
|
||||
}
|
||||
|
||||
#define DESTROY_IPC_FLAG (UINT32_C(1) << 31)
|
||||
|
||||
static void manager_unref_uid_internal(
|
||||
Manager *m,
|
||||
Hashmap **uid_refs,
|
||||
uid_t uid,
|
||||
bool destroy_now,
|
||||
int (*_clean_ipc)(uid_t uid)) {
|
||||
|
||||
uint32_t c, n;
|
||||
|
||||
assert(m);
|
||||
assert(uid_refs);
|
||||
assert(uid_is_valid(uid));
|
||||
assert(_clean_ipc);
|
||||
|
||||
/* A generic implementation, covering both manager_unref_uid() and manager_unref_gid(), under the assumption
|
||||
* that uid_t and gid_t are actually defined the same way, with the same validity rules.
|
||||
*
|
||||
* We store a hashmap where the UID/GID is they key and the value is a 32bit reference counter, whose highest
|
||||
* bit is used as flag for marking UIDs/GIDs whose IPC objects to remove when the last reference to the UID/GID
|
||||
* is dropped. The flag is set to on, once at least one reference from a unit where RemoveIPC= is set is added
|
||||
* on a UID/GID. It is reset when the UID's/GID's reference counter drops to 0 again. */
|
||||
|
||||
assert_cc(sizeof(uid_t) == sizeof(gid_t));
|
||||
assert_cc(UID_INVALID == (uid_t) GID_INVALID);
|
||||
|
||||
if (uid == 0) /* We don't keep track of root, and will never destroy it */
|
||||
return;
|
||||
|
||||
c = PTR_TO_UINT32(hashmap_get(*uid_refs, UID_TO_PTR(uid)));
|
||||
|
||||
n = c & ~DESTROY_IPC_FLAG;
|
||||
assert(n > 0);
|
||||
n--;
|
||||
|
||||
if (destroy_now && n == 0) {
|
||||
hashmap_remove(*uid_refs, UID_TO_PTR(uid));
|
||||
|
||||
if (c & DESTROY_IPC_FLAG) {
|
||||
log_debug("%s " UID_FMT " is no longer referenced, cleaning up its IPC.",
|
||||
_clean_ipc == clean_ipc_by_uid ? "UID" : "GID",
|
||||
uid);
|
||||
(void) _clean_ipc(uid);
|
||||
}
|
||||
} else {
|
||||
c = n | (c & DESTROY_IPC_FLAG);
|
||||
assert_se(hashmap_update(*uid_refs, UID_TO_PTR(uid), UINT32_TO_PTR(c)) >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
void manager_unref_uid(Manager *m, uid_t uid, bool destroy_now) {
|
||||
manager_unref_uid_internal(m, &m->uid_refs, uid, destroy_now, clean_ipc_by_uid);
|
||||
}
|
||||
|
||||
void manager_unref_gid(Manager *m, gid_t gid, bool destroy_now) {
|
||||
manager_unref_uid_internal(m, &m->gid_refs, (uid_t) gid, destroy_now, clean_ipc_by_gid);
|
||||
}
|
||||
|
||||
static int manager_ref_uid_internal(
|
||||
Manager *m,
|
||||
Hashmap **uid_refs,
|
||||
uid_t uid,
|
||||
bool clean_ipc) {
|
||||
|
||||
uint32_t c, n;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(uid_refs);
|
||||
assert(uid_is_valid(uid));
|
||||
|
||||
/* A generic implementation, covering both manager_ref_uid() and manager_ref_gid(), under the assumption
|
||||
* that uid_t and gid_t are actually defined the same way, with the same validity rules. */
|
||||
|
||||
assert_cc(sizeof(uid_t) == sizeof(gid_t));
|
||||
assert_cc(UID_INVALID == (uid_t) GID_INVALID);
|
||||
|
||||
if (uid == 0) /* We don't keep track of root, and will never destroy it */
|
||||
return 0;
|
||||
|
||||
r = hashmap_ensure_allocated(uid_refs, &trivial_hash_ops);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
c = PTR_TO_UINT32(hashmap_get(*uid_refs, UID_TO_PTR(uid)));
|
||||
|
||||
n = c & ~DESTROY_IPC_FLAG;
|
||||
n++;
|
||||
|
||||
if (n & DESTROY_IPC_FLAG) /* check for overflow */
|
||||
return -EOVERFLOW;
|
||||
|
||||
c = n | (c & DESTROY_IPC_FLAG) | (clean_ipc ? DESTROY_IPC_FLAG : 0);
|
||||
|
||||
return hashmap_replace(*uid_refs, UID_TO_PTR(uid), UINT32_TO_PTR(c));
|
||||
}
|
||||
|
||||
int manager_ref_uid(Manager *m, uid_t uid, bool clean_ipc) {
|
||||
return manager_ref_uid_internal(m, &m->uid_refs, uid, clean_ipc);
|
||||
}
|
||||
|
||||
int manager_ref_gid(Manager *m, gid_t gid, bool clean_ipc) {
|
||||
return manager_ref_uid_internal(m, &m->gid_refs, (uid_t) gid, clean_ipc);
|
||||
}
|
||||
|
||||
static void manager_vacuum_uid_refs_internal(
|
||||
Manager *m,
|
||||
Hashmap **uid_refs,
|
||||
int (*_clean_ipc)(uid_t uid)) {
|
||||
|
||||
Iterator i;
|
||||
void *p, *k;
|
||||
|
||||
assert(m);
|
||||
assert(uid_refs);
|
||||
assert(_clean_ipc);
|
||||
|
||||
HASHMAP_FOREACH_KEY(p, k, *uid_refs, i) {
|
||||
uint32_t c, n;
|
||||
uid_t uid;
|
||||
|
||||
uid = PTR_TO_UID(k);
|
||||
c = PTR_TO_UINT32(p);
|
||||
|
||||
n = c & ~DESTROY_IPC_FLAG;
|
||||
if (n > 0)
|
||||
continue;
|
||||
|
||||
if (c & DESTROY_IPC_FLAG) {
|
||||
log_debug("Found unreferenced %s " UID_FMT " after reload/reexec. Cleaning up.",
|
||||
_clean_ipc == clean_ipc_by_uid ? "UID" : "GID",
|
||||
uid);
|
||||
(void) _clean_ipc(uid);
|
||||
}
|
||||
|
||||
assert_se(hashmap_remove(*uid_refs, k) == p);
|
||||
}
|
||||
}
|
||||
|
||||
void manager_vacuum_uid_refs(Manager *m) {
|
||||
manager_vacuum_uid_refs_internal(m, &m->uid_refs, clean_ipc_by_uid);
|
||||
}
|
||||
|
||||
void manager_vacuum_gid_refs(Manager *m) {
|
||||
manager_vacuum_uid_refs_internal(m, &m->gid_refs, clean_ipc_by_gid);
|
||||
}
|
||||
|
||||
static void manager_serialize_uid_refs_internal(
|
||||
Manager *m,
|
||||
FILE *f,
|
||||
Hashmap **uid_refs,
|
||||
const char *field_name) {
|
||||
|
||||
Iterator i;
|
||||
void *p, *k;
|
||||
|
||||
assert(m);
|
||||
assert(f);
|
||||
assert(uid_refs);
|
||||
assert(field_name);
|
||||
|
||||
/* Serialize the UID reference table. Or actually, just the IPC destruction flag of it, as the actual counter
|
||||
* of it is better rebuild after a reload/reexec. */
|
||||
|
||||
HASHMAP_FOREACH_KEY(p, k, *uid_refs, i) {
|
||||
uint32_t c;
|
||||
uid_t uid;
|
||||
|
||||
uid = PTR_TO_UID(k);
|
||||
c = PTR_TO_UINT32(p);
|
||||
|
||||
if (!(c & DESTROY_IPC_FLAG))
|
||||
continue;
|
||||
|
||||
fprintf(f, "%s=" UID_FMT "\n", field_name, uid);
|
||||
}
|
||||
}
|
||||
|
||||
void manager_serialize_uid_refs(Manager *m, FILE *f) {
|
||||
manager_serialize_uid_refs_internal(m, f, &m->uid_refs, "destroy-ipc-uid");
|
||||
}
|
||||
|
||||
void manager_serialize_gid_refs(Manager *m, FILE *f) {
|
||||
manager_serialize_uid_refs_internal(m, f, &m->gid_refs, "destroy-ipc-gid");
|
||||
}
|
||||
|
||||
static void manager_deserialize_uid_refs_one_internal(
|
||||
Manager *m,
|
||||
Hashmap** uid_refs,
|
||||
const char *value) {
|
||||
|
||||
uid_t uid;
|
||||
uint32_t c;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(uid_refs);
|
||||
assert(value);
|
||||
|
||||
r = parse_uid(value, &uid);
|
||||
if (r < 0 || uid == 0) {
|
||||
log_debug("Unable to parse UID reference serialization");
|
||||
return;
|
||||
}
|
||||
|
||||
r = hashmap_ensure_allocated(uid_refs, &trivial_hash_ops);
|
||||
if (r < 0) {
|
||||
log_oom();
|
||||
return;
|
||||
}
|
||||
|
||||
c = PTR_TO_UINT32(hashmap_get(*uid_refs, UID_TO_PTR(uid)));
|
||||
if (c & DESTROY_IPC_FLAG)
|
||||
return;
|
||||
|
||||
c |= DESTROY_IPC_FLAG;
|
||||
|
||||
r = hashmap_replace(*uid_refs, UID_TO_PTR(uid), UINT32_TO_PTR(c));
|
||||
if (r < 0) {
|
||||
log_debug("Failed to add UID reference entry");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void manager_deserialize_uid_refs_one(Manager *m, const char *value) {
|
||||
manager_deserialize_uid_refs_one_internal(m, &m->uid_refs, value);
|
||||
}
|
||||
|
||||
void manager_deserialize_gid_refs_one(Manager *m, const char *value) {
|
||||
manager_deserialize_uid_refs_one_internal(m, &m->gid_refs, value);
|
||||
}
|
||||
|
||||
int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
|
||||
struct buffer {
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
char unit_name[UNIT_NAME_MAX+1];
|
||||
} _packed_ buffer;
|
||||
|
||||
Manager *m = userdata;
|
||||
ssize_t l;
|
||||
size_t n;
|
||||
Unit *u;
|
||||
|
||||
assert_se(source);
|
||||
assert_se(m);
|
||||
|
||||
/* Invoked whenever a child process succeeded resolving its user/group to use and sent us the resulting UID/GID
|
||||
* in a datagram. We parse the datagram here and pass it off to the unit, so that it can add a reference to the
|
||||
* UID/GID so that it can destroy the UID/GID's IPC objects when the reference counter drops to 0. */
|
||||
|
||||
l = recv(fd, &buffer, sizeof(buffer), MSG_DONTWAIT);
|
||||
if (l < 0) {
|
||||
if (errno == EINTR || errno == EAGAIN)
|
||||
return 0;
|
||||
|
||||
return log_error_errno(errno, "Failed to read from user lookup fd: %m");
|
||||
}
|
||||
|
||||
if ((size_t) l <= offsetof(struct buffer, unit_name)) {
|
||||
log_warning("Received too short user lookup message, ignoring.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((size_t) l > offsetof(struct buffer, unit_name) + UNIT_NAME_MAX) {
|
||||
log_warning("Received too long user lookup message, ignoring.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!uid_is_valid(buffer.uid) && !gid_is_valid(buffer.gid)) {
|
||||
log_warning("Got user lookup message with invalid UID/GID pair, ignoring.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
n = (size_t) l - offsetof(struct buffer, unit_name);
|
||||
if (memchr(buffer.unit_name, 0, n)) {
|
||||
log_warning("Received lookup message with embedded NUL character, ignoring.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
buffer.unit_name[n] = 0;
|
||||
u = manager_get_unit(m, buffer.unit_name);
|
||||
if (!u) {
|
||||
log_debug("Got user lookup message but unit doesn't exist, ignoring.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_unit_debug(u, "User lookup succeeded: uid=" UID_FMT " gid=" GID_FMT, buffer.uid, buffer.gid);
|
||||
|
||||
unit_notify_user_lookup(u, buffer.uid, buffer.gid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *const manager_state_table[_MANAGER_STATE_MAX] = {
|
||||
[MANAGER_INITIALIZING] = "initializing",
|
||||
[MANAGER_STARTING] = "starting",
|
||||
|
@ -143,6 +143,9 @@ struct Manager {
|
||||
|
||||
sd_event_source *jobs_in_progress_event_source;
|
||||
|
||||
int user_lookup_fds[2];
|
||||
sd_event_source *user_lookup_event_source;
|
||||
|
||||
UnitFileScope unit_file_scope;
|
||||
LookupPaths lookup_paths;
|
||||
Set *unit_path_cache;
|
||||
@ -234,7 +237,6 @@ struct Manager {
|
||||
bool dispatching_dbus_queue:1;
|
||||
|
||||
bool taint_usr:1;
|
||||
|
||||
bool test_run:1;
|
||||
|
||||
/* If non-zero, exit with the following value when the systemd
|
||||
@ -301,6 +303,10 @@ struct Manager {
|
||||
/* Dynamic users/groups, indexed by their name */
|
||||
Hashmap *dynamic_users;
|
||||
|
||||
/* Keep track of all UIDs and GIDs any of our services currently use. This is useful for the RemoveIPC= logic. */
|
||||
Hashmap *uid_refs;
|
||||
Hashmap *gid_refs;
|
||||
|
||||
/* When the user hits C-A-D more than 7 times per 2s, reboot immediately... */
|
||||
RateLimit ctrl_alt_del_ratelimit;
|
||||
|
||||
@ -378,5 +384,20 @@ ManagerState manager_state(Manager *m);
|
||||
|
||||
int manager_update_failed_units(Manager *m, Unit *u, bool failed);
|
||||
|
||||
void manager_unref_uid(Manager *m, uid_t uid, bool destroy_now);
|
||||
int manager_ref_uid(Manager *m, uid_t uid, bool clean_ipc);
|
||||
|
||||
void manager_unref_gid(Manager *m, gid_t gid, bool destroy_now);
|
||||
int manager_ref_gid(Manager *m, gid_t gid, bool destroy_now);
|
||||
|
||||
void manager_vacuum_uid_refs(Manager *m);
|
||||
void manager_vacuum_gid_refs(Manager *m);
|
||||
|
||||
void manager_serialize_uid_refs(Manager *m, FILE *f);
|
||||
void manager_deserialize_uid_refs_one(Manager *m, const char *value);
|
||||
|
||||
void manager_serialize_gid_refs(Manager *m, FILE *f);
|
||||
void manager_deserialize_gid_refs_one(Manager *m, const char *value);
|
||||
|
||||
const char *manager_state_to_string(ManagerState m) _const_;
|
||||
ManagerState manager_state_from_string(const char *s) _pure_;
|
||||
|
@ -769,6 +769,8 @@ static void mount_enter_dead(Mount *m, MountResult f) {
|
||||
|
||||
exec_context_destroy_runtime_directory(&m->exec_context, manager_get_runtime_prefix(UNIT(m)->manager));
|
||||
|
||||
unit_unref_uid_gid(UNIT(m), true);
|
||||
|
||||
dynamic_creds_destroy(&m->dynamic_creds);
|
||||
}
|
||||
|
||||
|
@ -1471,6 +1471,9 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
|
||||
/* Also, remove the runtime directory */
|
||||
exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager));
|
||||
|
||||
/* Get rid of the IPC bits of the user */
|
||||
unit_unref_uid_gid(UNIT(s), true);
|
||||
|
||||
/* Release the user, and destroy it if we are the only remaining owner */
|
||||
dynamic_creds_destroy(&s->dynamic_creds);
|
||||
|
||||
|
@ -1905,6 +1905,8 @@ static void socket_enter_dead(Socket *s, SocketResult f) {
|
||||
|
||||
exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager));
|
||||
|
||||
unit_unref_uid_gid(UNIT(s), true);
|
||||
|
||||
dynamic_creds_destroy(&s->dynamic_creds);
|
||||
}
|
||||
|
||||
|
@ -683,6 +683,8 @@ static void swap_enter_dead(Swap *s, SwapResult f) {
|
||||
|
||||
exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager));
|
||||
|
||||
unit_unref_uid_gid(UNIT(s), true);
|
||||
|
||||
dynamic_creds_destroy(&s->dynamic_creds);
|
||||
}
|
||||
|
||||
|
174
src/core/unit.c
174
src/core/unit.c
@ -100,7 +100,8 @@ Unit *unit_new(Manager *m, size_t size) {
|
||||
u->on_failure_job_mode = JOB_REPLACE;
|
||||
u->cgroup_inotify_wd = -1;
|
||||
u->job_timeout = USEC_INFINITY;
|
||||
u->sigchldgen = 0;
|
||||
u->ref_uid = UID_INVALID;
|
||||
u->ref_gid = GID_INVALID;
|
||||
|
||||
RATELIMIT_INIT(u->start_limit, m->default_start_limit_interval, m->default_start_limit_burst);
|
||||
RATELIMIT_INIT(u->auto_stop_ratelimit, 10 * USEC_PER_SEC, 16);
|
||||
@ -550,6 +551,8 @@ void unit_free(Unit *u) {
|
||||
|
||||
unit_release_cgroup(u);
|
||||
|
||||
unit_unref_uid_gid(u, false);
|
||||
|
||||
(void) manager_update_failed_units(u->manager, u, false);
|
||||
set_remove(u->manager->startup_units, u);
|
||||
|
||||
@ -2614,6 +2617,11 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
|
||||
unit_serialize_item(u, f, "cgroup", u->cgroup_path);
|
||||
unit_serialize_item(u, f, "cgroup-realized", yes_no(u->cgroup_realized));
|
||||
|
||||
if (uid_is_valid(u->ref_uid))
|
||||
unit_serialize_item_format(u, f, "ref-uid", UID_FMT, u->ref_uid);
|
||||
if (gid_is_valid(u->ref_gid))
|
||||
unit_serialize_item_format(u, f, "ref-gid", GID_FMT, u->ref_gid);
|
||||
|
||||
if (serialize_jobs) {
|
||||
if (u->job) {
|
||||
fprintf(f, "job\n");
|
||||
@ -2851,6 +2859,28 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
|
||||
u->cgroup_realized = b;
|
||||
|
||||
continue;
|
||||
|
||||
} else if (streq(l, "ref-uid")) {
|
||||
uid_t uid;
|
||||
|
||||
r = parse_uid(v, &uid);
|
||||
if (r < 0)
|
||||
log_unit_debug(u, "Failed to parse referenced UID %s, ignoring.", v);
|
||||
else
|
||||
unit_ref_uid_gid(u, uid, GID_INVALID);
|
||||
|
||||
continue;
|
||||
|
||||
} else if (streq(l, "ref-gid")) {
|
||||
gid_t gid;
|
||||
|
||||
r = parse_gid(v, &gid);
|
||||
if (r < 0)
|
||||
log_unit_debug(u, "Failed to parse referenced GID %s, ignoring.", v);
|
||||
else
|
||||
unit_ref_uid_gid(u, UID_INVALID, gid);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (unit_can_serialize(u)) {
|
||||
@ -3310,6 +3340,7 @@ int unit_patch_contexts(Unit *u) {
|
||||
}
|
||||
|
||||
ec->private_tmp = true;
|
||||
ec->remove_ipc = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3932,3 +3963,144 @@ pid_t unit_main_pid(Unit *u) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void unit_unref_uid_internal(
|
||||
Unit *u,
|
||||
uid_t *ref_uid,
|
||||
bool destroy_now,
|
||||
void (*_manager_unref_uid)(Manager *m, uid_t uid, bool destroy_now)) {
|
||||
|
||||
assert(u);
|
||||
assert(ref_uid);
|
||||
assert(_manager_unref_uid);
|
||||
|
||||
/* Generic implementation of both unit_unref_uid() and unit_unref_gid(), under the assumption that uid_t and
|
||||
* gid_t are actually the same time, with the same validity rules.
|
||||
*
|
||||
* Drops a reference to UID/GID from a unit. */
|
||||
|
||||
assert_cc(sizeof(uid_t) == sizeof(gid_t));
|
||||
assert_cc(UID_INVALID == (uid_t) GID_INVALID);
|
||||
|
||||
if (!uid_is_valid(*ref_uid))
|
||||
return;
|
||||
|
||||
_manager_unref_uid(u->manager, *ref_uid, destroy_now);
|
||||
*ref_uid = UID_INVALID;
|
||||
}
|
||||
|
||||
void unit_unref_uid(Unit *u, bool destroy_now) {
|
||||
unit_unref_uid_internal(u, &u->ref_uid, destroy_now, manager_unref_uid);
|
||||
}
|
||||
|
||||
void unit_unref_gid(Unit *u, bool destroy_now) {
|
||||
unit_unref_uid_internal(u, (uid_t*) &u->ref_gid, destroy_now, manager_unref_gid);
|
||||
}
|
||||
|
||||
static int unit_ref_uid_internal(
|
||||
Unit *u,
|
||||
uid_t *ref_uid,
|
||||
uid_t uid,
|
||||
bool clean_ipc,
|
||||
int (*_manager_ref_uid)(Manager *m, uid_t uid, bool clean_ipc)) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
assert(ref_uid);
|
||||
assert(uid_is_valid(uid));
|
||||
assert(_manager_ref_uid);
|
||||
|
||||
/* Generic implementation of both unit_ref_uid() and unit_ref_guid(), under the assumption that uid_t and gid_t
|
||||
* are actually the same type, and have the same validity rules.
|
||||
*
|
||||
* Adds a reference on a specific UID/GID to this unit. Each unit referencing the same UID/GID maintains a
|
||||
* reference so that we can destroy the UID/GID's IPC resources as soon as this is requested and the counter
|
||||
* drops to zero. */
|
||||
|
||||
assert_cc(sizeof(uid_t) == sizeof(gid_t));
|
||||
assert_cc(UID_INVALID == (uid_t) GID_INVALID);
|
||||
|
||||
if (*ref_uid == uid)
|
||||
return 0;
|
||||
|
||||
if (uid_is_valid(*ref_uid)) /* Already set? */
|
||||
return -EBUSY;
|
||||
|
||||
r = _manager_ref_uid(u->manager, uid, clean_ipc);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ref_uid = uid;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int unit_ref_uid(Unit *u, uid_t uid, bool clean_ipc) {
|
||||
return unit_ref_uid_internal(u, &u->ref_uid, uid, clean_ipc, manager_ref_uid);
|
||||
}
|
||||
|
||||
int unit_ref_gid(Unit *u, gid_t gid, bool clean_ipc) {
|
||||
return unit_ref_uid_internal(u, (uid_t*) &u->ref_gid, (uid_t) gid, clean_ipc, manager_ref_gid);
|
||||
}
|
||||
|
||||
static int unit_ref_uid_gid_internal(Unit *u, uid_t uid, gid_t gid, bool clean_ipc) {
|
||||
int r = 0, q = 0;
|
||||
|
||||
assert(u);
|
||||
|
||||
/* Reference both a UID and a GID in one go. Either references both, or neither. */
|
||||
|
||||
if (uid_is_valid(uid)) {
|
||||
r = unit_ref_uid(u, uid, clean_ipc);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (gid_is_valid(gid)) {
|
||||
q = unit_ref_gid(u, gid, clean_ipc);
|
||||
if (q < 0) {
|
||||
if (r > 0)
|
||||
unit_unref_uid(u, false);
|
||||
|
||||
return q;
|
||||
}
|
||||
}
|
||||
|
||||
return r > 0 || q > 0;
|
||||
}
|
||||
|
||||
int unit_ref_uid_gid(Unit *u, uid_t uid, gid_t gid) {
|
||||
ExecContext *c;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
|
||||
c = unit_get_exec_context(u);
|
||||
|
||||
r = unit_ref_uid_gid_internal(u, uid, gid, c ? c->remove_ipc : false);
|
||||
if (r < 0)
|
||||
return log_unit_warning_errno(u, r, "Couldn't add UID/GID reference to unit, proceeding without: %m");
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void unit_unref_uid_gid(Unit *u, bool destroy_now) {
|
||||
assert(u);
|
||||
|
||||
unit_unref_uid(u, destroy_now);
|
||||
unit_unref_gid(u, destroy_now);
|
||||
}
|
||||
|
||||
void unit_notify_user_lookup(Unit *u, uid_t uid, gid_t gid) {
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
|
||||
/* This is invoked whenever one of the forked off processes let's us know the UID/GID its user name/group names
|
||||
* resolved to. We keep track of which UID/GID is currently assigned in order to be able to destroy its IPC
|
||||
* objects when no service references the UID/GID anymore. */
|
||||
|
||||
r = unit_ref_uid_gid(u, uid, gid);
|
||||
if (r > 0)
|
||||
bus_unit_send_change_signal(u);
|
||||
}
|
||||
|
@ -180,6 +180,10 @@ struct Unit {
|
||||
/* Make sure we never enter endless loops with the check unneeded logic, or the BindsTo= logic */
|
||||
RateLimit auto_stop_ratelimit;
|
||||
|
||||
/* Reference to a specific UID/GID */
|
||||
uid_t ref_uid;
|
||||
gid_t ref_gid;
|
||||
|
||||
/* Cached unit file state and preset */
|
||||
UnitFileState unit_file_state;
|
||||
int unit_file_preset;
|
||||
@ -195,8 +199,6 @@ struct Unit {
|
||||
CGroupMask cgroup_members_mask;
|
||||
int cgroup_inotify_wd;
|
||||
|
||||
uint32_t cgroup_netclass_id;
|
||||
|
||||
/* How to start OnFailure units */
|
||||
JobMode on_failure_job_mode;
|
||||
|
||||
@ -373,8 +375,7 @@ struct UnitVTable {
|
||||
/* Called whenever a process of this unit sends us a message */
|
||||
void (*notify_message)(Unit *u, pid_t pid, char **tags, FDSet *fds);
|
||||
|
||||
/* Called whenever a name this Unit registered for comes or
|
||||
* goes away. */
|
||||
/* Called whenever a name this Unit registered for comes or goes away. */
|
||||
void (*bus_name_owner_change)(Unit *u, const char *name, const char *old_owner, const char *new_owner);
|
||||
|
||||
/* Called for each property that is being set */
|
||||
@ -623,6 +624,17 @@ int unit_fail_if_symlink(Unit *u, const char* where);
|
||||
|
||||
int unit_start_limit_test(Unit *u);
|
||||
|
||||
void unit_unref_uid(Unit *u, bool destroy_now);
|
||||
int unit_ref_uid(Unit *u, uid_t uid, bool clean_ipc);
|
||||
|
||||
void unit_unref_gid(Unit *u, bool destroy_now);
|
||||
int unit_ref_gid(Unit *u, gid_t gid, bool clean_ipc);
|
||||
|
||||
int unit_ref_uid_gid(Unit *u, uid_t uid, gid_t gid);
|
||||
void unit_unref_uid_gid(Unit *u, bool destroy_now);
|
||||
|
||||
void unit_notify_user_lookup(Unit *u, uid_t uid, gid_t gid);
|
||||
|
||||
/* Macros which append UNIT= or USER_UNIT= to the message */
|
||||
|
||||
#define log_unit_full(unit, level, error, ...) \
|
||||
|
@ -612,9 +612,14 @@ int user_finalize(User *u) {
|
||||
if (k < 0)
|
||||
r = k;
|
||||
|
||||
/* Clean SysV + POSIX IPC objects */
|
||||
if (u->manager->remove_ipc) {
|
||||
k = clean_ipc(u->uid);
|
||||
/* Clean SysV + POSIX IPC objects, but only if this is not a system user. Background: in many setups cronjobs
|
||||
* are run in full PAM and thus logind sessions, even if the code run doesn't belong to actual users but to
|
||||
* system components. Since enable RemoveIPC= globally for all users, we need to be a bit careful with such
|
||||
* cases, as we shouldn't accidentally remove a system service's IPC objects while it is running, just because
|
||||
* a cronjob running as the same user just finished. Hence: exclude system users generally from IPC clean-up,
|
||||
* and do it only for normal users. */
|
||||
if (u->manager->remove_ipc && u->uid > SYSTEM_UID_MAX) {
|
||||
k = clean_ipc_by_uid(u->uid);
|
||||
if (k < 0)
|
||||
r = k;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "bus-common-errors.h"
|
||||
#include "env-util.h"
|
||||
#include "hostname-util.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "macro.h"
|
||||
@ -434,6 +435,12 @@ enum nss_status _nss_mymachines_getpwnam_r(
|
||||
if (!machine_name_is_valid(machine))
|
||||
goto not_found;
|
||||
|
||||
if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0)
|
||||
/* Make sure we can't deadlock if we are invoked by dbus-daemon. This way, it won't be able to resolve
|
||||
* these UIDs, but that should be unproblematic as containers should never be able to connect to a bus
|
||||
* running on the host. */
|
||||
goto not_found;
|
||||
|
||||
r = sd_bus_open_system(&bus);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
@ -514,6 +521,9 @@ enum nss_status _nss_mymachines_getpwuid_r(
|
||||
if (uid < HOST_UID_LIMIT)
|
||||
goto not_found;
|
||||
|
||||
if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0)
|
||||
goto not_found;
|
||||
|
||||
r = sd_bus_open_system(&bus);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
@ -605,6 +615,9 @@ enum nss_status _nss_mymachines_getgrnam_r(
|
||||
if (!machine_name_is_valid(machine))
|
||||
goto not_found;
|
||||
|
||||
if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0)
|
||||
goto not_found;
|
||||
|
||||
r = sd_bus_open_system(&bus);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
@ -682,6 +695,9 @@ enum nss_status _nss_mymachines_getgrgid_r(
|
||||
if (gid < HOST_GID_LIMIT)
|
||||
goto not_found;
|
||||
|
||||
if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0)
|
||||
goto not_found;
|
||||
|
||||
r = sd_bus_open_system(&bus);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
@ -21,11 +21,14 @@
|
||||
|
||||
#include "sd-bus.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "bus-common-errors.h"
|
||||
#include "env-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "macro.h"
|
||||
#include "nss-util.h"
|
||||
#include "signal-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "string-util.h"
|
||||
#include "user-util.h"
|
||||
#include "util.h"
|
||||
@ -75,15 +78,50 @@ static const struct group nobody_group = {
|
||||
NSS_GETPW_PROTOTYPES(systemd);
|
||||
NSS_GETGR_PROTOTYPES(systemd);
|
||||
|
||||
static int direct_lookup_name(const char *name, uid_t *ret) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
const char *path;
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
|
||||
/* Normally, we go via the bus to resolve names. That has the benefit that it is available from any mount
|
||||
* namespace and subject to proper authentication. However, there's one problem: if our module is called from
|
||||
* dbus-daemon itself we really can't use D-Bus to communicate. In this case, resort to a client-side hack,
|
||||
* and look for the dynamic names directly. This is pretty ugly, but breaks the cyclic dependency. */
|
||||
|
||||
path = strjoina("/run/systemd/dynamic-uid/direct:", name);
|
||||
r = readlink_malloc(path, &s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return parse_uid(s, ret);
|
||||
}
|
||||
|
||||
static int direct_lookup_uid(uid_t uid, char **ret) {
|
||||
char path[strlen("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1], *s;
|
||||
int r;
|
||||
|
||||
xsprintf(path, "/run/systemd/dynamic-uid/direct:" UID_FMT, uid);
|
||||
|
||||
r = readlink_malloc(path, &s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!valid_user_group_name(s)) { /* extra safety check */
|
||||
free(s);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*ret = s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum nss_status _nss_systemd_getpwnam_r(
|
||||
const char *name,
|
||||
struct passwd *pwd,
|
||||
char *buffer, size_t buflen,
|
||||
int *errnop) {
|
||||
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
uint32_t translated;
|
||||
size_t l;
|
||||
int r;
|
||||
@ -114,6 +152,20 @@ enum nss_status _nss_systemd_getpwnam_r(
|
||||
if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
|
||||
goto not_found;
|
||||
|
||||
if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) {
|
||||
|
||||
/* Access the dynamic UID allocation directly if we are called from dbus-daemon, see above. */
|
||||
r = direct_lookup_name(name, (uid_t*) &translated);
|
||||
if (r == -ENOENT)
|
||||
goto not_found;
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
} else {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
|
||||
r = sd_bus_open_system(&bus);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
@ -137,6 +189,7 @@ enum nss_status _nss_systemd_getpwnam_r(
|
||||
r = sd_bus_message_read(reply, "u", &translated);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
l = strlen(name);
|
||||
if (buflen < l+1) {
|
||||
@ -175,6 +228,7 @@ enum nss_status _nss_systemd_getpwuid_r(
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
_cleanup_free_ char *direct = NULL;
|
||||
const char *translated;
|
||||
size_t l;
|
||||
int r;
|
||||
@ -204,6 +258,17 @@ enum nss_status _nss_systemd_getpwuid_r(
|
||||
if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
|
||||
goto not_found;
|
||||
|
||||
if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) {
|
||||
|
||||
r = direct_lookup_uid(uid, &direct);
|
||||
if (r == -ENOENT)
|
||||
goto not_found;
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
translated = direct;
|
||||
|
||||
} else {
|
||||
r = sd_bus_open_system(&bus);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
@ -227,6 +292,7 @@ enum nss_status _nss_systemd_getpwuid_r(
|
||||
r = sd_bus_message_read(reply, "s", &translated);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
l = strlen(translated) + 1;
|
||||
if (buflen < l) {
|
||||
@ -262,9 +328,6 @@ enum nss_status _nss_systemd_getgrnam_r(
|
||||
char *buffer, size_t buflen,
|
||||
int *errnop) {
|
||||
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
uint32_t translated;
|
||||
size_t l;
|
||||
int r;
|
||||
@ -294,6 +357,20 @@ enum nss_status _nss_systemd_getgrnam_r(
|
||||
if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
|
||||
goto not_found;
|
||||
|
||||
if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) {
|
||||
|
||||
/* Access the dynamic GID allocation directly if we are called from dbus-daemon, see above. */
|
||||
r = direct_lookup_name(name, (uid_t*) &translated);
|
||||
if (r == -ENOENT)
|
||||
goto not_found;
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
} else {
|
||||
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
|
||||
r = sd_bus_open_system(&bus);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
@ -317,6 +394,7 @@ enum nss_status _nss_systemd_getgrnam_r(
|
||||
r = sd_bus_message_read(reply, "u", &translated);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
l = sizeof(char*) + strlen(name) + 1;
|
||||
if (buflen < l) {
|
||||
@ -353,6 +431,7 @@ enum nss_status _nss_systemd_getgrgid_r(
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
_cleanup_free_ char *direct = NULL;
|
||||
const char *translated;
|
||||
size_t l;
|
||||
int r;
|
||||
@ -382,6 +461,16 @@ enum nss_status _nss_systemd_getgrgid_r(
|
||||
if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
|
||||
goto not_found;
|
||||
|
||||
if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) {
|
||||
|
||||
r = direct_lookup_uid(gid, &direct);
|
||||
if (r == -ENOENT)
|
||||
goto not_found;
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
translated = direct;
|
||||
} else {
|
||||
r = sd_bus_open_system(&bus);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
@ -405,6 +494,7 @@ enum nss_status _nss_systemd_getgrgid_r(
|
||||
r = sd_bus_message_read(reply, "s", &translated);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
l = sizeof(char*) + strlen(translated) + 1;
|
||||
if (buflen < l) {
|
||||
|
@ -204,7 +204,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||
"IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit",
|
||||
"PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", "NoNewPrivileges",
|
||||
"SyslogLevelPrefix", "Delegate", "RemainAfterElapse", "MemoryDenyWriteExecute",
|
||||
"RestrictRealtime", "DynamicUser")) {
|
||||
"RestrictRealtime", "DynamicUser", "RemoveIPC")) {
|
||||
|
||||
r = parse_boolean(eq);
|
||||
if (r < 0)
|
||||
|
@ -41,8 +41,20 @@
|
||||
#include "macro.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "user-util.h"
|
||||
|
||||
static int clean_sysvipc_shm(uid_t delete_uid) {
|
||||
static bool match_uid_gid(uid_t subject_uid, gid_t subject_gid, uid_t delete_uid, gid_t delete_gid) {
|
||||
|
||||
if (uid_is_valid(delete_uid) && subject_uid == delete_uid)
|
||||
return true;
|
||||
|
||||
if (gid_is_valid(delete_gid) && subject_gid == delete_gid)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int clean_sysvipc_shm(uid_t delete_uid, gid_t delete_gid) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
char line[LINE_MAX];
|
||||
bool first = true;
|
||||
@ -77,7 +89,7 @@ static int clean_sysvipc_shm(uid_t delete_uid) {
|
||||
if (n_attached > 0)
|
||||
continue;
|
||||
|
||||
if (uid != delete_uid)
|
||||
if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
|
||||
continue;
|
||||
|
||||
if (shmctl(shmid, IPC_RMID, NULL) < 0) {
|
||||
@ -89,7 +101,8 @@ static int clean_sysvipc_shm(uid_t delete_uid) {
|
||||
ret = log_warning_errno(errno,
|
||||
"Failed to remove SysV shared memory segment %i: %m",
|
||||
shmid);
|
||||
}
|
||||
} else
|
||||
log_debug("Removed SysV shared memory segment %i.", shmid);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -98,7 +111,7 @@ fail:
|
||||
return log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m");
|
||||
}
|
||||
|
||||
static int clean_sysvipc_sem(uid_t delete_uid) {
|
||||
static int clean_sysvipc_sem(uid_t delete_uid, gid_t delete_gid) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
char line[LINE_MAX];
|
||||
bool first = true;
|
||||
@ -128,7 +141,7 @@ static int clean_sysvipc_sem(uid_t delete_uid) {
|
||||
&semid, &uid, &gid, &cuid, &cgid) != 5)
|
||||
continue;
|
||||
|
||||
if (uid != delete_uid)
|
||||
if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
|
||||
continue;
|
||||
|
||||
if (semctl(semid, 0, IPC_RMID) < 0) {
|
||||
@ -140,7 +153,8 @@ static int clean_sysvipc_sem(uid_t delete_uid) {
|
||||
ret = log_warning_errno(errno,
|
||||
"Failed to remove SysV semaphores object %i: %m",
|
||||
semid);
|
||||
}
|
||||
} else
|
||||
log_debug("Removed SysV semaphore %i.", semid);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -149,7 +163,7 @@ fail:
|
||||
return log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m");
|
||||
}
|
||||
|
||||
static int clean_sysvipc_msg(uid_t delete_uid) {
|
||||
static int clean_sysvipc_msg(uid_t delete_uid, gid_t delete_gid) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
char line[LINE_MAX];
|
||||
bool first = true;
|
||||
@ -180,7 +194,7 @@ static int clean_sysvipc_msg(uid_t delete_uid) {
|
||||
&msgid, &cpid, &lpid, &uid, &gid, &cuid, &cgid) != 7)
|
||||
continue;
|
||||
|
||||
if (uid != delete_uid)
|
||||
if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
|
||||
continue;
|
||||
|
||||
if (msgctl(msgid, IPC_RMID, NULL) < 0) {
|
||||
@ -192,7 +206,8 @@ static int clean_sysvipc_msg(uid_t delete_uid) {
|
||||
ret = log_warning_errno(errno,
|
||||
"Failed to remove SysV message queue %i: %m",
|
||||
msgid);
|
||||
}
|
||||
} else
|
||||
log_debug("Removed SysV message queue %i.", msgid);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -201,13 +216,13 @@ fail:
|
||||
return log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m");
|
||||
}
|
||||
|
||||
static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
|
||||
static int clean_posix_shm_internal(DIR *dir, uid_t uid, gid_t gid) {
|
||||
struct dirent *de;
|
||||
int ret = 0, r;
|
||||
|
||||
assert(dir);
|
||||
|
||||
FOREACH_DIRENT(de, dir, goto fail) {
|
||||
FOREACH_DIRENT_ALL(de, dir, goto fail) {
|
||||
struct stat st;
|
||||
|
||||
if (STR_IN_SET(de->d_name, "..", "."))
|
||||
@ -217,12 +232,11 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
|
||||
if (errno == ENOENT)
|
||||
continue;
|
||||
|
||||
log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name);
|
||||
ret = -errno;
|
||||
ret = log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (st.st_uid != uid)
|
||||
if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid))
|
||||
continue;
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
@ -230,12 +244,10 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
|
||||
|
||||
kid = xopendirat(dirfd(dir), de->d_name, O_NOFOLLOW|O_NOATIME);
|
||||
if (!kid) {
|
||||
if (errno != ENOENT) {
|
||||
log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name);
|
||||
ret = -errno;
|
||||
}
|
||||
if (errno != ENOENT)
|
||||
ret = log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name);
|
||||
} else {
|
||||
r = clean_posix_shm_internal(kid, uid);
|
||||
r = clean_posix_shm_internal(kid, uid, gid);
|
||||
if (r < 0)
|
||||
ret = r;
|
||||
}
|
||||
@ -245,9 +257,9 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
|
||||
if (errno == ENOENT)
|
||||
continue;
|
||||
|
||||
log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name);
|
||||
ret = -errno;
|
||||
}
|
||||
ret = log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name);
|
||||
} else
|
||||
log_debug("Removed POSIX shared memory directory %s", de->d_name);
|
||||
} else {
|
||||
|
||||
if (unlinkat(dirfd(dir), de->d_name, 0) < 0) {
|
||||
@ -255,20 +267,19 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
|
||||
if (errno == ENOENT)
|
||||
continue;
|
||||
|
||||
log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name);
|
||||
ret = -errno;
|
||||
}
|
||||
ret = log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name);
|
||||
} else
|
||||
log_debug("Removed POSIX shared memory segment %s", de->d_name);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
log_warning_errno(errno, "Failed to read /dev/shm: %m");
|
||||
return -errno;
|
||||
return log_warning_errno(errno, "Failed to read /dev/shm: %m");
|
||||
}
|
||||
|
||||
static int clean_posix_shm(uid_t uid) {
|
||||
static int clean_posix_shm(uid_t uid, gid_t gid) {
|
||||
_cleanup_closedir_ DIR *dir = NULL;
|
||||
|
||||
dir = opendir("/dev/shm");
|
||||
@ -279,10 +290,10 @@ static int clean_posix_shm(uid_t uid) {
|
||||
return log_warning_errno(errno, "Failed to open /dev/shm: %m");
|
||||
}
|
||||
|
||||
return clean_posix_shm_internal(dir, uid);
|
||||
return clean_posix_shm_internal(dir, uid, gid);
|
||||
}
|
||||
|
||||
static int clean_posix_mq(uid_t uid) {
|
||||
static int clean_posix_mq(uid_t uid, gid_t gid) {
|
||||
_cleanup_closedir_ DIR *dir = NULL;
|
||||
struct dirent *de;
|
||||
int ret = 0;
|
||||
@ -295,7 +306,7 @@ static int clean_posix_mq(uid_t uid) {
|
||||
return log_warning_errno(errno, "Failed to open /dev/mqueue: %m");
|
||||
}
|
||||
|
||||
FOREACH_DIRENT(de, dir, goto fail) {
|
||||
FOREACH_DIRENT_ALL(de, dir, goto fail) {
|
||||
struct stat st;
|
||||
char fn[1+strlen(de->d_name)+1];
|
||||
|
||||
@ -312,7 +323,7 @@ static int clean_posix_mq(uid_t uid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (st.st_uid != uid)
|
||||
if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid))
|
||||
continue;
|
||||
|
||||
fn[0] = '/';
|
||||
@ -325,7 +336,8 @@ static int clean_posix_mq(uid_t uid) {
|
||||
ret = log_warning_errno(errno,
|
||||
"Failed to unlink POSIX message queue %s: %m",
|
||||
fn);
|
||||
}
|
||||
} else
|
||||
log_debug("Removed POSIX message queue %s", fn);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -334,32 +346,44 @@ fail:
|
||||
return log_warning_errno(errno, "Failed to read /dev/mqueue: %m");
|
||||
}
|
||||
|
||||
int clean_ipc(uid_t uid) {
|
||||
int clean_ipc(uid_t uid, gid_t gid) {
|
||||
int ret = 0, r;
|
||||
|
||||
/* Refuse to clean IPC of the root and system users */
|
||||
if (uid <= SYSTEM_UID_MAX)
|
||||
/* Anything to do? */
|
||||
if (!uid_is_valid(uid) && !gid_is_valid(gid))
|
||||
return 0;
|
||||
|
||||
r = clean_sysvipc_shm(uid);
|
||||
/* Refuse to clean IPC of the root user */
|
||||
if (uid == 0 && gid == 0)
|
||||
return 0;
|
||||
|
||||
r = clean_sysvipc_shm(uid, gid);
|
||||
if (r < 0)
|
||||
ret = r;
|
||||
|
||||
r = clean_sysvipc_sem(uid);
|
||||
r = clean_sysvipc_sem(uid, gid);
|
||||
if (r < 0)
|
||||
ret = r;
|
||||
|
||||
r = clean_sysvipc_msg(uid);
|
||||
r = clean_sysvipc_msg(uid, gid);
|
||||
if (r < 0)
|
||||
ret = r;
|
||||
|
||||
r = clean_posix_shm(uid);
|
||||
r = clean_posix_shm(uid, gid);
|
||||
if (r < 0)
|
||||
ret = r;
|
||||
|
||||
r = clean_posix_mq(uid);
|
||||
r = clean_posix_mq(uid, gid);
|
||||
if (r < 0)
|
||||
ret = r;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int clean_ipc_by_uid(uid_t uid) {
|
||||
return clean_ipc(uid, GID_INVALID);
|
||||
}
|
||||
|
||||
int clean_ipc_by_gid(gid_t gid) {
|
||||
return clean_ipc(UID_INVALID, gid);
|
||||
}
|
||||
|
@ -21,4 +21,6 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
int clean_ipc(uid_t uid);
|
||||
int clean_ipc(uid_t uid, gid_t gid);
|
||||
int clean_ipc_by_uid(uid_t uid);
|
||||
int clean_ipc_by_gid(gid_t gid);
|
||||
|
@ -32,5 +32,5 @@ int main(int argc, char *argv[]) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return clean_ipc(uid) < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
return clean_ipc_by_uid(uid) < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user