1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2024-10-28 20:25:25 +03:00

Merge pull request #3865 from poettering/remove-ipc

add RemoveIPC= service file setting
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2016-08-18 22:52:13 -04:00 committed by GitHub
commit 57e3375f83
25 changed files with 1057 additions and 186 deletions

View File

@ -160,14 +160,14 @@
use. However, UID/GIDs are recycled after a unit is terminated. Care should be taken that any processes running 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 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 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 these files or directories. If <varname>DynamicUser=</varname> is enabled, <varname>RemoveIPC=</varname> and
implied. This ensures that the lifetime of temporary files created by the executed processes is bound to the <varname>PrivateTmp=</varname> are implied. This ensures that the lifetime of IPC objects and temporary files
runtime of the service, and hence the lifetime of the dynamic user/group. Since <filename>/tmp</filename> and created by the executed processes is bound to the runtime of the service, and hence the lifetime of the dynamic
<filename>/var/tmp</filename> are usually the only world-writable directories on a system this ensures that a user/group. Since <filename>/tmp</filename> and <filename>/var/tmp</filename> are usually the only
unit making use of dynamic user/group allocation cannot leave files around after unit termination. Use world-writable directories on a system this ensures that a unit making use of dynamic user/group allocation
<varname>RuntimeDirectory=</varname> (see below) in order to assign a writable runtime directory to a service, cannot leave files around after unit termination. Use <varname>RuntimeDirectory=</varname> (see below) in order
owned by the dynamic user/group and removed automatically when the unit is terminated. Defaults to to assign a writable runtime directory to a service, owned by the dynamic user/group and removed automatically
off.</para></listitem> when the unit is terminated. Defaults to off.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
@ -185,6 +185,18 @@
user. This does not affect commands prefixed with <literal>+</literal>.</para></listitem> user. This does not affect commands prefixed with <literal>+</literal>.</para></listitem>
</varlistentry> </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> <varlistentry>
<term><varname>Nice=</varname></term> <term><varname>Nice=</varname></term>
@ -920,27 +932,19 @@
<varlistentry> <varlistentry>
<term><varname>PrivateTmp=</varname></term> <term><varname>PrivateTmp=</varname></term>
<listitem><para>Takes a boolean argument. If true, sets up a <listitem><para>Takes a boolean argument. If true, sets up a new file system namespace for the executed
new file system namespace for the executed processes and processes and mounts private <filename>/tmp</filename> and <filename>/var/tmp</filename> directories inside it
mounts private <filename>/tmp</filename> and that is not shared by processes outside of the namespace. This is useful to secure access to temporary files of
<filename>/var/tmp</filename> directories inside it that is the process, but makes sharing between processes via <filename>/tmp</filename> or <filename>/var/tmp</filename>
not shared by processes outside of the namespace. This is impossible. If this is enabled, all temporary files created by a service in these directories will be removed
useful to secure access to temporary files of the process, but after the service is stopped. Defaults to false. It is possible to run two or more units within the same
makes sharing between processes via <filename>/tmp</filename> private <filename>/tmp</filename> and <filename>/var/tmp</filename> namespace by using the
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 <varname>JoinsNamespaceOf=</varname> directive, see
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry> <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
for details. Note that using this setting will disconnect details. Note that using this setting will disconnect propagation of mounts from the service to the host
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
(propagation in the opposite direction continues to work). services which shall be able to install mount points in the main mount namespace. This setting is implied if
This means that this setting may not be used for services <varname>DynamicUser=</varname> is set.</para></listitem>
which shall be able to install mount points in the main mount
namespace.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>

View File

@ -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("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("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("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("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("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), 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", "IgnoreSIGPIPE", "TTYVHangup", "TTYReset",
"PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
"NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute", "NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute",
"RestrictRealtime", "DynamicUser")) { "RestrictRealtime", "DynamicUser", "RemoveIPC")) {
int b; int b;
r = sd_bus_message_read(message, "b", &b); r = sd_bus_message_read(message, "b", &b);
@ -1103,6 +1104,8 @@ int bus_exec_context_set_transient_property(
c->restrict_realtime = b; c->restrict_realtime = b;
else if (streq(name, "DynamicUser")) else if (streq(name, "DynamicUser"))
c->dynamic_user = b; 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)); unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, yes_no(b));
} }

View File

@ -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("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("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("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("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("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), BUS_EXEC_COMMAND_VTABLE("ExecRemount", offsetof(Mount, exec_command[MOUNT_EXEC_REMOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),

View File

@ -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("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), 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), 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("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("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), 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("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("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("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_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("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), 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("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("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), 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 SD_BUS_VTABLE_END
}; };

View File

@ -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("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("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("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("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("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), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPre", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),

View File

@ -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("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("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("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("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), BUS_EXEC_COMMAND_VTABLE("ExecDeactivate", offsetof(Swap, exec_command[SWAP_EXEC_DEACTIVATE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
SD_BUS_VTABLE_END SD_BUS_VTABLE_END

View File

@ -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("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("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("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("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("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), 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("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("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("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("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("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), 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("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), 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 SD_BUS_VTABLE_END
}; };

View File

@ -150,6 +150,42 @@ int dynamic_user_acquire(Manager *m, const char *name, DynamicUser** ret) {
return 1; 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 int pick_uid(const char *name, uid_t *ret_uid) {
static const uint8_t hash_key[] = { 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) ftruncate(lock_fd, l);
(void) make_uid_symlinks(candidate, name, true); /* also add direct lookup symlinks */
*ret_uid = candidate; *ret_uid = candidate;
r = lock_fd; r = lock_fd;
@ -324,14 +361,16 @@ static int dynamic_user_push(DynamicUser *d, uid_t uid, int lock_fd) {
return 0; 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]; char lock_path[strlen("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1];
if (lock_fd < 0) if (lock_fd < 0)
return; return;
xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, uid); 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) { 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. */ /* 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) { 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; return -errno;
} }
@ -407,7 +446,7 @@ int dynamic_user_realize(DynamicUser *d, uid_t *ret) {
if (r < 0) { if (r < 0) {
if (r != -EAGAIN) { if (r != -EAGAIN) {
/* OK, something bad happened, let's get rid of the bits we acquired. */ /* 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; 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 /* 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. */ * 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); safe_close(uid_lock_fd);
uid = new_uid; uid = new_uid;
@ -513,7 +552,7 @@ static int dynamic_user_close(DynamicUser *d) {
goto finish; goto finish;
/* This dynamic user was realized and dynamically allocated. In this case, let's remove the lock file. */ /* 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; r = 1;
finish: finish:

View File

@ -91,6 +91,7 @@
#include "selinux-util.h" #include "selinux-util.h"
#include "signal-util.h" #include "signal-util.h"
#include "smack-util.h" #include "smack-util.h"
#include "special.h"
#include "string-table.h" #include "string-table.h"
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"
@ -1384,6 +1385,7 @@ static void do_idle_pipe_dance(int idle_pipe[4]) {
} }
static int build_environment( static int build_environment(
Unit *u,
const ExecContext *c, const ExecContext *c,
const ExecParameters *p, const ExecParameters *p,
unsigned n_fds, unsigned n_fds,
@ -1401,7 +1403,7 @@ static int build_environment(
assert(c); assert(c);
assert(ret); assert(ret);
our_env = new0(char*, 12); our_env = new0(char*, 13);
if (!our_env) if (!our_env)
return -ENOMEM; return -ENOMEM;
@ -1436,6 +1438,16 @@ static int build_environment(
our_env[n_env++] = x; 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) { if (home) {
x = strappend("HOME=", home); x = strappend("HOME=", home);
if (!x) if (!x)
@ -1723,11 +1735,12 @@ static int close_remaining_fds(
const ExecParameters *params, const ExecParameters *params,
ExecRuntime *runtime, ExecRuntime *runtime,
DynamicCreds *dcreds, DynamicCreds *dcreds,
int user_lookup_fd,
int socket_fd, int socket_fd,
int *fds, unsigned n_fds) { int *fds, unsigned n_fds) {
unsigned n_dont_close = 0; unsigned n_dont_close = 0;
int dont_close[n_fds + 11]; int dont_close[n_fds + 12];
assert(params); assert(params);
@ -1755,9 +1768,40 @@ static int close_remaining_fds(
append_socket_pair(dont_close, &n_dont_close, dcreds->group->storage_socket); 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); 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( static int exec_child(
Unit *unit, Unit *unit,
ExecCommand *command, ExecCommand *command,
@ -1769,6 +1813,7 @@ static int exec_child(
int socket_fd, int socket_fd,
int *fds, unsigned n_fds, int *fds, unsigned n_fds,
char **files_env, char **files_env,
int user_lookup_fd,
int *exit_status) { int *exit_status) {
_cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **accum_env = NULL, **final_argv = NULL; _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(); 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) { if (r < 0) {
*exit_status = EXIT_FDS; *exit_status = EXIT_FDS;
return r; return r;
@ -1862,7 +1907,7 @@ static int exec_child(
return r; return r;
} }
if (uid == UID_INVALID || gid == GID_INVALID) { if (!uid_is_valid(uid) || !gid_is_valid(gid)) {
*exit_status = EXIT_USER; *exit_status = EXIT_USER;
return -ESRCH; 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 /* If a socket is connected to STDIN/STDOUT/STDERR, we
* must sure to drop O_NONBLOCK */ * must sure to drop O_NONBLOCK */
if (socket_fd >= 0) if (socket_fd >= 0)
@ -2059,6 +2112,7 @@ static int exec_child(
} }
r = build_environment( r = build_environment(
unit,
context, context,
params, params,
n_fds, n_fds,
@ -2501,6 +2555,7 @@ int exec_spawn(Unit *unit,
socket_fd, socket_fd,
fds, n_fds, fds, n_fds,
files_env, files_env,
unit->manager->user_lookup_fds[1],
&exit_status); &exit_status);
if (r < 0) { if (r < 0) {
log_open(); log_open();

View File

@ -178,6 +178,7 @@ struct ExecContext {
bool no_new_privileges; bool no_new_privileges;
bool dynamic_user; bool dynamic_user;
bool remove_ipc;
/* This is not exposed to the user but available /* This is not exposed to the user but available
* internally. We need it to make sure that whenever we spawn * internally. We need it to make sure that whenever we spawn

View File

@ -45,6 +45,7 @@
#include "bus-error.h" #include "bus-error.h"
#include "bus-kernel.h" #include "bus-kernel.h"
#include "bus-util.h" #include "bus-util.h"
#include "clean-ipc.h"
#include "dbus-job.h" #include "dbus-job.h"
#include "dbus-manager.h" #include "dbus-manager.h"
#include "dbus-unit.h" #include "dbus-unit.h"
@ -81,6 +82,7 @@
#include "transaction.h" #include "transaction.h"
#include "umask-util.h" #include "umask-util.h"
#include "unit-name.h" #include "unit-name.h"
#include "user-util.h"
#include "util.h" #include "util.h"
#include "virt.h" #include "virt.h"
#include "watchdog.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_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_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_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_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_dispatch_run_queue(sd_event_source *source, void *userdata);
static int manager_run_generators(Manager *m); 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->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->cgroup_inotify_fd =
m->ask_password_inotify_fd = -1; 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->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 */ m->have_ask_password = -EINVAL; /* we don't know */
@ -812,6 +817,59 @@ static int manager_setup_cgroups_agent(Manager *m) {
return 0; 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) { static int manager_connect_bus(Manager *m, bool reexecuting) {
bool try_bus_connect; bool try_bus_connect;
@ -853,8 +911,7 @@ enum {
_GC_OFFSET_MAX _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; Iterator i;
Unit *other; 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->time_change_event_source);
sd_event_source_unref(m->jobs_in_progress_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->run_queue_event_source);
sd_event_source_unref(m->user_lookup_event_source);
safe_close(m->signal_fd); safe_close(m->signal_fd);
safe_close(m->notify_fd); safe_close(m->notify_fd);
safe_close(m->cgroups_agent_fd); safe_close(m->cgroups_agent_fd);
safe_close(m->time_change_fd); safe_close(m->time_change_fd);
safe_close(m->kdbus_fd); safe_close(m->kdbus_fd);
safe_close_pair(m->user_lookup_fds);
manager_close_ask_password(m); manager_close_ask_password(m);
@ -1052,6 +1111,9 @@ Manager* manager_free(Manager *m) {
assert(hashmap_isempty(m->units_requiring_mounts_for)); assert(hashmap_isempty(m->units_requiring_mounts_for));
hashmap_free(m->units_requiring_mounts_for); hashmap_free(m->units_requiring_mounts_for);
hashmap_free(m->uid_refs);
hashmap_free(m->gid_refs);
free(m); free(m);
return NULL; return NULL;
} }
@ -1221,6 +1283,10 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
if (q < 0 && r == 0) if (q < 0 && r == 0)
r = q; 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 /* We might have deserialized the kdbus control fd, but if we
* didn't, then let's create the bus now. */ * didn't, then let's create the bus now. */
manager_connect_bus(m, !!serialization); 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 */ /* Release any dynamic users no longer referenced */
dynamic_user_vacuum(m, true); 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) { if (serialization) {
assert(m->n_reloading > 0); assert(m->n_reloading > 0);
m->n_reloading--; 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); 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) { if (m->kdbus_fd >= 0) {
int copy; int copy;
@ -2412,6 +2496,9 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
if (r < 0) if (r < 0)
return r; return r;
manager_serialize_uid_refs(m, f);
manager_serialize_gid_refs(m, f);
fputc('\n', f); fputc('\n', f);
HASHMAP_FOREACH_KEY(u, t, m->units, i) { 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); 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=")) { } else if (startswith(l, "kdbus-fd=")) {
int fd; int fd;
@ -2590,6 +2689,10 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
} else if (startswith(l, "dynamic-user=")) } else if (startswith(l, "dynamic-user="))
dynamic_user_deserialize_one(m, l + 13, fds); 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 { else {
int k; int k;
@ -2672,6 +2775,8 @@ int manager_reload(Manager *m) {
lookup_paths_flush_generator(&m->lookup_paths); lookup_paths_flush_generator(&m->lookup_paths);
lookup_paths_free(&m->lookup_paths); lookup_paths_free(&m->lookup_paths);
dynamic_user_vacuum(m, false); 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); q = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL);
if (q < 0 && r >= 0) if (q < 0 && r >= 0)
@ -2705,12 +2810,20 @@ int manager_reload(Manager *m) {
if (q < 0 && r >= 0) if (q < 0 && r >= 0)
r = q; r = q;
q = manager_setup_user_lookup_fd(m);
if (q < 0 && r >= 0)
r = q;
/* Third, fire things up! */ /* Third, fire things up! */
manager_coldplug(m); manager_coldplug(m);
/* Release any dynamic users no longer referenced */ /* Release any dynamic users no longer referenced */
dynamic_user_vacuum(m, true); 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 */ /* Sync current state of bus names with our set of listening units */
if (m->api_bus) if (m->api_bus)
manager_sync_bus_names(m, m->api_bus); manager_sync_bus_names(m, m->api_bus);
@ -3144,6 +3257,300 @@ ManagerState manager_state(Manager *m) {
return MANAGER_RUNNING; 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] = { static const char *const manager_state_table[_MANAGER_STATE_MAX] = {
[MANAGER_INITIALIZING] = "initializing", [MANAGER_INITIALIZING] = "initializing",
[MANAGER_STARTING] = "starting", [MANAGER_STARTING] = "starting",

View File

@ -143,6 +143,9 @@ struct Manager {
sd_event_source *jobs_in_progress_event_source; sd_event_source *jobs_in_progress_event_source;
int user_lookup_fds[2];
sd_event_source *user_lookup_event_source;
UnitFileScope unit_file_scope; UnitFileScope unit_file_scope;
LookupPaths lookup_paths; LookupPaths lookup_paths;
Set *unit_path_cache; Set *unit_path_cache;
@ -234,7 +237,6 @@ struct Manager {
bool dispatching_dbus_queue:1; bool dispatching_dbus_queue:1;
bool taint_usr:1; bool taint_usr:1;
bool test_run:1; bool test_run:1;
/* If non-zero, exit with the following value when the systemd /* If non-zero, exit with the following value when the systemd
@ -301,6 +303,10 @@ struct Manager {
/* Dynamic users/groups, indexed by their name */ /* Dynamic users/groups, indexed by their name */
Hashmap *dynamic_users; 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... */ /* When the user hits C-A-D more than 7 times per 2s, reboot immediately... */
RateLimit ctrl_alt_del_ratelimit; 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); 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_; const char *manager_state_to_string(ManagerState m) _const_;
ManagerState manager_state_from_string(const char *s) _pure_; ManagerState manager_state_from_string(const char *s) _pure_;

View File

@ -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)); 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); dynamic_creds_destroy(&m->dynamic_creds);
} }

View File

@ -1471,6 +1471,9 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
/* Also, remove the runtime directory */ /* Also, remove the runtime directory */
exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager)); 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 */ /* Release the user, and destroy it if we are the only remaining owner */
dynamic_creds_destroy(&s->dynamic_creds); dynamic_creds_destroy(&s->dynamic_creds);

View File

@ -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)); 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); dynamic_creds_destroy(&s->dynamic_creds);
} }

View File

@ -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)); 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); dynamic_creds_destroy(&s->dynamic_creds);
} }

View File

@ -100,7 +100,8 @@ Unit *unit_new(Manager *m, size_t size) {
u->on_failure_job_mode = JOB_REPLACE; u->on_failure_job_mode = JOB_REPLACE;
u->cgroup_inotify_wd = -1; u->cgroup_inotify_wd = -1;
u->job_timeout = USEC_INFINITY; 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->start_limit, m->default_start_limit_interval, m->default_start_limit_burst);
RATELIMIT_INIT(u->auto_stop_ratelimit, 10 * USEC_PER_SEC, 16); 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_release_cgroup(u);
unit_unref_uid_gid(u, false);
(void) manager_update_failed_units(u->manager, u, false); (void) manager_update_failed_units(u->manager, u, false);
set_remove(u->manager->startup_units, u); 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", u->cgroup_path);
unit_serialize_item(u, f, "cgroup-realized", yes_no(u->cgroup_realized)); 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 (serialize_jobs) {
if (u->job) { if (u->job) {
fprintf(f, "job\n"); fprintf(f, "job\n");
@ -2851,6 +2859,28 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
u->cgroup_realized = b; u->cgroup_realized = b;
continue; 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)) { if (unit_can_serialize(u)) {
@ -3310,6 +3340,7 @@ int unit_patch_contexts(Unit *u) {
} }
ec->private_tmp = true; ec->private_tmp = true;
ec->remove_ipc = true;
} }
} }
@ -3932,3 +3963,144 @@ pid_t unit_main_pid(Unit *u) {
return 0; 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);
}

View File

@ -180,6 +180,10 @@ struct Unit {
/* Make sure we never enter endless loops with the check unneeded logic, or the BindsTo= logic */ /* Make sure we never enter endless loops with the check unneeded logic, or the BindsTo= logic */
RateLimit auto_stop_ratelimit; RateLimit auto_stop_ratelimit;
/* Reference to a specific UID/GID */
uid_t ref_uid;
gid_t ref_gid;
/* Cached unit file state and preset */ /* Cached unit file state and preset */
UnitFileState unit_file_state; UnitFileState unit_file_state;
int unit_file_preset; int unit_file_preset;
@ -195,8 +199,6 @@ struct Unit {
CGroupMask cgroup_members_mask; CGroupMask cgroup_members_mask;
int cgroup_inotify_wd; int cgroup_inotify_wd;
uint32_t cgroup_netclass_id;
/* How to start OnFailure units */ /* How to start OnFailure units */
JobMode on_failure_job_mode; JobMode on_failure_job_mode;
@ -373,8 +375,7 @@ struct UnitVTable {
/* Called whenever a process of this unit sends us a message */ /* Called whenever a process of this unit sends us a message */
void (*notify_message)(Unit *u, pid_t pid, char **tags, FDSet *fds); void (*notify_message)(Unit *u, pid_t pid, char **tags, FDSet *fds);
/* Called whenever a name this Unit registered for comes or /* Called whenever a name this Unit registered for comes or goes away. */
* goes away. */
void (*bus_name_owner_change)(Unit *u, const char *name, const char *old_owner, const char *new_owner); 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 */ /* 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); 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 */ /* Macros which append UNIT= or USER_UNIT= to the message */
#define log_unit_full(unit, level, error, ...) \ #define log_unit_full(unit, level, error, ...) \

View File

@ -612,9 +612,14 @@ int user_finalize(User *u) {
if (k < 0) if (k < 0)
r = k; r = k;
/* Clean SysV + POSIX IPC objects */ /* Clean SysV + POSIX IPC objects, but only if this is not a system user. Background: in many setups cronjobs
if (u->manager->remove_ipc) { * are run in full PAM and thus logind sessions, even if the code run doesn't belong to actual users but to
k = clean_ipc(u->uid); * 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) if (k < 0)
r = k; r = k;
} }

View File

@ -25,6 +25,7 @@
#include "alloc-util.h" #include "alloc-util.h"
#include "bus-common-errors.h" #include "bus-common-errors.h"
#include "env-util.h"
#include "hostname-util.h" #include "hostname-util.h"
#include "in-addr-util.h" #include "in-addr-util.h"
#include "macro.h" #include "macro.h"
@ -434,6 +435,12 @@ enum nss_status _nss_mymachines_getpwnam_r(
if (!machine_name_is_valid(machine)) if (!machine_name_is_valid(machine))
goto not_found; 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); r = sd_bus_open_system(&bus);
if (r < 0) if (r < 0)
goto fail; goto fail;
@ -514,6 +521,9 @@ enum nss_status _nss_mymachines_getpwuid_r(
if (uid < HOST_UID_LIMIT) if (uid < HOST_UID_LIMIT)
goto not_found; goto not_found;
if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0)
goto not_found;
r = sd_bus_open_system(&bus); r = sd_bus_open_system(&bus);
if (r < 0) if (r < 0)
goto fail; goto fail;
@ -605,6 +615,9 @@ enum nss_status _nss_mymachines_getgrnam_r(
if (!machine_name_is_valid(machine)) if (!machine_name_is_valid(machine))
goto not_found; goto not_found;
if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0)
goto not_found;
r = sd_bus_open_system(&bus); r = sd_bus_open_system(&bus);
if (r < 0) if (r < 0)
goto fail; goto fail;
@ -682,6 +695,9 @@ enum nss_status _nss_mymachines_getgrgid_r(
if (gid < HOST_GID_LIMIT) if (gid < HOST_GID_LIMIT)
goto not_found; goto not_found;
if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0)
goto not_found;
r = sd_bus_open_system(&bus); r = sd_bus_open_system(&bus);
if (r < 0) if (r < 0)
goto fail; goto fail;

View File

@ -21,11 +21,14 @@
#include "sd-bus.h" #include "sd-bus.h"
#include "alloc-util.h"
#include "bus-common-errors.h" #include "bus-common-errors.h"
#include "env-util.h" #include "env-util.h"
#include "fs-util.h"
#include "macro.h" #include "macro.h"
#include "nss-util.h" #include "nss-util.h"
#include "signal-util.h" #include "signal-util.h"
#include "stdio-util.h"
#include "string-util.h" #include "string-util.h"
#include "user-util.h" #include "user-util.h"
#include "util.h" #include "util.h"
@ -75,15 +78,50 @@ static const struct group nobody_group = {
NSS_GETPW_PROTOTYPES(systemd); NSS_GETPW_PROTOTYPES(systemd);
NSS_GETGR_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( enum nss_status _nss_systemd_getpwnam_r(
const char *name, const char *name,
struct passwd *pwd, struct passwd *pwd,
char *buffer, size_t buflen, char *buffer, size_t buflen,
int *errnop) { 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; uint32_t translated;
size_t l; size_t l;
int r; int r;
@ -114,30 +152,45 @@ enum nss_status _nss_systemd_getpwnam_r(
if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
goto not_found; goto not_found;
r = sd_bus_open_system(&bus); if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) {
if (r < 0)
goto fail;
r = sd_bus_call_method(bus, /* Access the dynamic UID allocation directly if we are called from dbus-daemon, see above. */
"org.freedesktop.systemd1", r = direct_lookup_name(name, (uid_t*) &translated);
"/org/freedesktop/systemd1", if (r == -ENOENT)
"org.freedesktop.systemd1.Manager",
"LookupDynamicUserByName",
&error,
&reply,
"s",
name);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
goto not_found; goto not_found;
if (r < 0)
goto fail;
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;
r = sd_bus_call_method(bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"LookupDynamicUserByName",
&error,
&reply,
"s",
name);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
goto not_found;
goto fail;
}
r = sd_bus_message_read(reply, "u", &translated);
if (r < 0)
goto fail;
} }
r = sd_bus_message_read(reply, "u", &translated);
if (r < 0)
goto fail;
l = strlen(name); l = strlen(name);
if (buflen < l+1) { if (buflen < l+1) {
*errnop = ENOMEM; *errnop = ENOMEM;
@ -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_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_free_ char *direct = NULL;
const char *translated; const char *translated;
size_t l; size_t l;
int r; int r;
@ -204,30 +258,42 @@ enum nss_status _nss_systemd_getpwuid_r(
if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
goto not_found; goto not_found;
r = sd_bus_open_system(&bus); if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) {
if (r < 0)
goto fail;
r = sd_bus_call_method(bus, r = direct_lookup_uid(uid, &direct);
"org.freedesktop.systemd1", if (r == -ENOENT)
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"LookupDynamicUserByUID",
&error,
&reply,
"u",
(uint32_t) uid);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
goto not_found; goto not_found;
if (r < 0)
goto fail;
goto fail; translated = direct;
} else {
r = sd_bus_open_system(&bus);
if (r < 0)
goto fail;
r = sd_bus_call_method(bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"LookupDynamicUserByUID",
&error,
&reply,
"u",
(uint32_t) uid);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
goto not_found;
goto fail;
}
r = sd_bus_message_read(reply, "s", &translated);
if (r < 0)
goto fail;
} }
r = sd_bus_message_read(reply, "s", &translated);
if (r < 0)
goto fail;
l = strlen(translated) + 1; l = strlen(translated) + 1;
if (buflen < l) { if (buflen < l) {
*errnop = ENOMEM; *errnop = ENOMEM;
@ -262,9 +328,6 @@ enum nss_status _nss_systemd_getgrnam_r(
char *buffer, size_t buflen, char *buffer, size_t buflen,
int *errnop) { 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; uint32_t translated;
size_t l; size_t l;
int r; int r;
@ -294,30 +357,45 @@ enum nss_status _nss_systemd_getgrnam_r(
if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
goto not_found; goto not_found;
r = sd_bus_open_system(&bus); if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) {
if (r < 0)
goto fail;
r = sd_bus_call_method(bus, /* Access the dynamic GID allocation directly if we are called from dbus-daemon, see above. */
"org.freedesktop.systemd1", r = direct_lookup_name(name, (uid_t*) &translated);
"/org/freedesktop/systemd1", if (r == -ENOENT)
"org.freedesktop.systemd1.Manager",
"LookupDynamicUserByName",
&error,
&reply,
"s",
name);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
goto not_found; goto not_found;
if (r < 0)
goto fail;
} else {
goto fail; _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;
r = sd_bus_call_method(bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"LookupDynamicUserByName",
&error,
&reply,
"s",
name);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
goto not_found;
goto fail;
}
r = sd_bus_message_read(reply, "u", &translated);
if (r < 0)
goto fail;
} }
r = sd_bus_message_read(reply, "u", &translated);
if (r < 0)
goto fail;
l = sizeof(char*) + strlen(name) + 1; l = sizeof(char*) + strlen(name) + 1;
if (buflen < l) { if (buflen < l) {
*errnop = ENOMEM; *errnop = ENOMEM;
@ -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_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_free_ char *direct = NULL;
const char *translated; const char *translated;
size_t l; size_t l;
int r; int r;
@ -382,30 +461,41 @@ enum nss_status _nss_systemd_getgrgid_r(
if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
goto not_found; goto not_found;
r = sd_bus_open_system(&bus); if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) {
if (r < 0)
goto fail;
r = sd_bus_call_method(bus, r = direct_lookup_uid(gid, &direct);
"org.freedesktop.systemd1", if (r == -ENOENT)
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"LookupDynamicUserByUID",
&error,
&reply,
"u",
(uint32_t) gid);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
goto not_found; goto not_found;
if (r < 0)
goto fail;
goto fail; translated = direct;
} else {
r = sd_bus_open_system(&bus);
if (r < 0)
goto fail;
r = sd_bus_call_method(bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"LookupDynamicUserByUID",
&error,
&reply,
"u",
(uint32_t) gid);
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
goto not_found;
goto fail;
}
r = sd_bus_message_read(reply, "s", &translated);
if (r < 0)
goto fail;
} }
r = sd_bus_message_read(reply, "s", &translated);
if (r < 0)
goto fail;
l = sizeof(char*) + strlen(translated) + 1; l = sizeof(char*) + strlen(translated) + 1;
if (buflen < l) { if (buflen < l) {
*errnop = ENOMEM; *errnop = ENOMEM;

View File

@ -204,7 +204,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
"IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit", "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit",
"PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", "NoNewPrivileges", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", "NoNewPrivileges",
"SyslogLevelPrefix", "Delegate", "RemainAfterElapse", "MemoryDenyWriteExecute", "SyslogLevelPrefix", "Delegate", "RemainAfterElapse", "MemoryDenyWriteExecute",
"RestrictRealtime", "DynamicUser")) { "RestrictRealtime", "DynamicUser", "RemoveIPC")) {
r = parse_boolean(eq); r = parse_boolean(eq);
if (r < 0) if (r < 0)

View File

@ -41,8 +41,20 @@
#include "macro.h" #include "macro.h"
#include "string-util.h" #include "string-util.h"
#include "strv.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; _cleanup_fclose_ FILE *f = NULL;
char line[LINE_MAX]; char line[LINE_MAX];
bool first = true; bool first = true;
@ -77,7 +89,7 @@ static int clean_sysvipc_shm(uid_t delete_uid) {
if (n_attached > 0) if (n_attached > 0)
continue; continue;
if (uid != delete_uid) if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
continue; continue;
if (shmctl(shmid, IPC_RMID, NULL) < 0) { 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, ret = log_warning_errno(errno,
"Failed to remove SysV shared memory segment %i: %m", "Failed to remove SysV shared memory segment %i: %m",
shmid); shmid);
} } else
log_debug("Removed SysV shared memory segment %i.", shmid);
} }
return ret; return ret;
@ -98,7 +111,7 @@ fail:
return log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m"); 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; _cleanup_fclose_ FILE *f = NULL;
char line[LINE_MAX]; char line[LINE_MAX];
bool first = true; bool first = true;
@ -128,7 +141,7 @@ static int clean_sysvipc_sem(uid_t delete_uid) {
&semid, &uid, &gid, &cuid, &cgid) != 5) &semid, &uid, &gid, &cuid, &cgid) != 5)
continue; continue;
if (uid != delete_uid) if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
continue; continue;
if (semctl(semid, 0, IPC_RMID) < 0) { 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, ret = log_warning_errno(errno,
"Failed to remove SysV semaphores object %i: %m", "Failed to remove SysV semaphores object %i: %m",
semid); semid);
} } else
log_debug("Removed SysV semaphore %i.", semid);
} }
return ret; return ret;
@ -149,7 +163,7 @@ fail:
return log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m"); 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; _cleanup_fclose_ FILE *f = NULL;
char line[LINE_MAX]; char line[LINE_MAX];
bool first = true; bool first = true;
@ -180,7 +194,7 @@ static int clean_sysvipc_msg(uid_t delete_uid) {
&msgid, &cpid, &lpid, &uid, &gid, &cuid, &cgid) != 7) &msgid, &cpid, &lpid, &uid, &gid, &cuid, &cgid) != 7)
continue; continue;
if (uid != delete_uid) if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
continue; continue;
if (msgctl(msgid, IPC_RMID, NULL) < 0) { 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, ret = log_warning_errno(errno,
"Failed to remove SysV message queue %i: %m", "Failed to remove SysV message queue %i: %m",
msgid); msgid);
} } else
log_debug("Removed SysV message queue %i.", msgid);
} }
return ret; return ret;
@ -201,13 +216,13 @@ fail:
return log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m"); 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; struct dirent *de;
int ret = 0, r; int ret = 0, r;
assert(dir); assert(dir);
FOREACH_DIRENT(de, dir, goto fail) { FOREACH_DIRENT_ALL(de, dir, goto fail) {
struct stat st; struct stat st;
if (STR_IN_SET(de->d_name, "..", ".")) 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) if (errno == ENOENT)
continue; continue;
log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name); ret = log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name);
ret = -errno;
continue; continue;
} }
if (st.st_uid != uid) if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid))
continue; continue;
if (S_ISDIR(st.st_mode)) { 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); kid = xopendirat(dirfd(dir), de->d_name, O_NOFOLLOW|O_NOATIME);
if (!kid) { if (!kid) {
if (errno != ENOENT) { if (errno != ENOENT)
log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name); ret = log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name);
ret = -errno;
}
} else { } else {
r = clean_posix_shm_internal(kid, uid); r = clean_posix_shm_internal(kid, uid, gid);
if (r < 0) if (r < 0)
ret = r; ret = r;
} }
@ -245,9 +257,9 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
if (errno == ENOENT) if (errno == ENOENT)
continue; continue;
log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name); ret = log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name);
ret = -errno; } else
} log_debug("Removed POSIX shared memory directory %s", de->d_name);
} else { } else {
if (unlinkat(dirfd(dir), de->d_name, 0) < 0) { 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) if (errno == ENOENT)
continue; continue;
log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name); ret = log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name);
ret = -errno; } else
} log_debug("Removed POSIX shared memory segment %s", de->d_name);
} }
} }
return ret; return ret;
fail: fail:
log_warning_errno(errno, "Failed to read /dev/shm: %m"); return log_warning_errno(errno, "Failed to read /dev/shm: %m");
return -errno;
} }
static int clean_posix_shm(uid_t uid) { static int clean_posix_shm(uid_t uid, gid_t gid) {
_cleanup_closedir_ DIR *dir = NULL; _cleanup_closedir_ DIR *dir = NULL;
dir = opendir("/dev/shm"); 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 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; _cleanup_closedir_ DIR *dir = NULL;
struct dirent *de; struct dirent *de;
int ret = 0; 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"); 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; struct stat st;
char fn[1+strlen(de->d_name)+1]; char fn[1+strlen(de->d_name)+1];
@ -312,7 +323,7 @@ static int clean_posix_mq(uid_t uid) {
continue; continue;
} }
if (st.st_uid != uid) if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid))
continue; continue;
fn[0] = '/'; fn[0] = '/';
@ -325,7 +336,8 @@ static int clean_posix_mq(uid_t uid) {
ret = log_warning_errno(errno, ret = log_warning_errno(errno,
"Failed to unlink POSIX message queue %s: %m", "Failed to unlink POSIX message queue %s: %m",
fn); fn);
} } else
log_debug("Removed POSIX message queue %s", fn);
} }
return ret; return ret;
@ -334,32 +346,44 @@ fail:
return log_warning_errno(errno, "Failed to read /dev/mqueue: %m"); 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; int ret = 0, r;
/* Refuse to clean IPC of the root and system users */ /* Anything to do? */
if (uid <= SYSTEM_UID_MAX) if (!uid_is_valid(uid) && !gid_is_valid(gid))
return 0; 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) if (r < 0)
ret = r; ret = r;
r = clean_sysvipc_sem(uid); r = clean_sysvipc_sem(uid, gid);
if (r < 0) if (r < 0)
ret = r; ret = r;
r = clean_sysvipc_msg(uid); r = clean_sysvipc_msg(uid, gid);
if (r < 0) if (r < 0)
ret = r; ret = r;
r = clean_posix_shm(uid); r = clean_posix_shm(uid, gid);
if (r < 0) if (r < 0)
ret = r; ret = r;
r = clean_posix_mq(uid); r = clean_posix_mq(uid, gid);
if (r < 0) if (r < 0)
ret = r; ret = r;
return ret; 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);
}

View File

@ -21,4 +21,6 @@
#include <sys/types.h> #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);

View File

@ -32,5 +32,5 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
return clean_ipc(uid) < 0 ? EXIT_FAILURE : EXIT_SUCCESS; return clean_ipc_by_uid(uid) < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
} }