1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2025-01-08 21:17:47 +03:00

Merge pull request #12056 from poettering/seccomp-suid-sgid

Introduce RestrictSUIDSGID= for disabling SUID/SGID file creation
This commit is contained in:
Lennart Poettering 2019-04-02 17:30:11 +02:00 committed by GitHub
commit 82c604607f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 466 additions and 30 deletions

15
NEWS
View File

@ -201,6 +201,21 @@ CHANGES WITH 242 in spe:
done anymore, and instead calling `systemctl preset-all` is
recommended after the first installation of systemd.
* A new boolean sandboxing option RestrictSUIDSGID= has been added that
is built on seccomp. When turned on creation of SUID/SGID files is
prohibited.
* The NoNewPrivileges= and the new RestrictSUIDSGID= options are now
implied if DynamicUser= is turned on for a service. This hardens
these services, so that they neither can benefit from nor create
SUID/SGID executables. This is a minor compatibility breakage, given
that when DynamicUser= was first introduced SUID/SGID behaviour was
unaffected. However, the security benefit of these two options is
substantial, and the setting is still relatively new, hence we opted
to make it mandatory for services with dynamic users.
CHANGES WITH 241:
* The default locale can now be configured at compile time. Otherwise,

3
TODO
View File

@ -32,9 +32,6 @@ Features:
- Make sure resume= and resume_offset= on the kernel cmdline always take
precedence
* maybe add a seccomp-based high-level filter that blocks creation of suid/sgid
files.
* make MAINPID= message reception checks even stricter: if service uses User=,
then check sending UID and ignore message if it doesn't match the user or
root.

View File

@ -155,6 +155,7 @@ All execution-related settings are available for transient units.
✓ MemoryDenyWriteExecute=
✓ RestrictNamespaces=
✓ RestrictRealtime=
✓ RestrictSUIDSGID=
✓ RestrictAddressFamilies=
✓ LockPersonality=
✓ LimitCPU=

View File

@ -252,7 +252,9 @@
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. Moreover <varname>ProtectSystem=strict</varname> and
termination. Furthermore <varname>NoNewPrivileges=</varname> and <varname>RestrictSUIDSGID=</varname>
are implicitly enabled to ensure that processes invoked cannot take benefit or create SUID/SGID files
or directories. Moreover <varname>ProtectSystem=strict</varname> and
<varname>ProtectHome=read-only</varname> are implied, thus prohibiting the service to write to
arbitrary file system locations. In order to allow the service to write to certain directories, they
have to be whitelisted using <varname>ReadWritePaths=</varname>, but care must be taken so that
@ -379,19 +381,21 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
<varlistentry>
<term><varname>NoNewPrivileges=</varname></term>
<listitem><para>Takes a boolean argument. If true, ensures that the service process and all its children can
never gain new privileges through <function>execve()</function> (e.g. via setuid or setgid bits, or filesystem
capabilities). This is the simplest and most effective way to ensure that a process and its children can never
elevate privileges again. Defaults to false, but certain settings override this and ignore the value of this
setting. This is the case when <varname>SystemCallFilter=</varname>,
<varname>SystemCallArchitectures=</varname>, <varname>RestrictAddressFamilies=</varname>,
<varname>RestrictNamespaces=</varname>, <varname>PrivateDevices=</varname>,
<varname>ProtectKernelTunables=</varname>, <varname>ProtectKernelModules=</varname>,
<varname>MemoryDenyWriteExecute=</varname>, <varname>RestrictRealtime=</varname>, or
<varname>LockPersonality=</varname> are specified. Note that even if this setting is overridden by them,
<command>systemctl show</command> shows the original value of this setting. Also see
<ulink url="https://www.kernel.org/doc/html/latest/userspace-api/no_new_privs.html">No New Privileges
Flag</ulink>. </para></listitem>
<listitem><para>Takes a boolean argument. If true, ensures that the service process and all its
children can never gain new privileges through <function>execve()</function> (e.g. via setuid or
setgid bits, or filesystem capabilities). This is the simplest and most effective way to ensure that
a process and its children can never elevate privileges again. Defaults to false, but certain
settings override this and ignore the value of this setting. This is the case when
<varname>SystemCallFilter=</varname>, <varname>SystemCallArchitectures=</varname>,
<varname>RestrictAddressFamilies=</varname>, <varname>RestrictNamespaces=</varname>,
<varname>PrivateDevices=</varname>, <varname>ProtectKernelTunables=</varname>,
<varname>ProtectKernelModules=</varname>, <varname>MemoryDenyWriteExecute=</varname>,
<varname>RestrictRealtime=</varname>, <varname>RestrictSUIDSGID=</varname>,
<varname>DynamicUser=</varname> or <varname>LockPersonality=</varname> are specified. Note that even
if this setting is overridden by them, <command>systemctl show</command> shows the original value of
this setting. Also see <ulink
url="https://www.kernel.org/doc/html/latest/userspace-api/no_new_privs.html">No New Privileges
Flag</ulink>.</para></listitem>
</varlistentry>
<varlistentry>
@ -1392,6 +1396,23 @@ RestrictNamespaces=~cgroup net</programlisting>
that actually require them. Defaults to off.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>RestrictSUIDSGID=</varname></term>
<listitem><para>Takes a boolean argument. If set, any attempts to set the set-user-ID (SUID) or
set-group-ID (SGID) bits on files or directories will be denied (for details on these bits see
<citerefentry
project='man-pages'><refentrytitle>inode</refentrytitle><manvolnum>7</manvolnum></citerefentry>). If
running in user mode, or in system mode, but without the <constant>CAP_SYS_ADMIN</constant>
capability (e.g. setting <varname>User=</varname>), <varname>NoNewPrivileges=yes</varname> is
implied. As the SUID/SGID bits are mechanisms to elevate privileges, and allows users to acquire the
identity of other users, it is recommended to restrict creation of SUID/SGID files to the few
programs that actually require them. Note that this restricts marking of any type of file system
object with these bits, including both regular files and directories (where the SGID is a different
meaning than for files, see documentation). This option is implied if <varname>DynamicUser=</varname>
is enabled. Defaults to off.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>RemoveIPC=</varname></term>

View File

@ -75,6 +75,7 @@ struct security_info {
uint64_t restrict_namespaces;
bool restrict_realtime;
bool restrict_suid_sgid;
char *root_directory;
char *root_image;
@ -1148,6 +1149,16 @@ static const struct security_assessor security_assessor_table[] = {
.assess = assess_bool,
.offset = offsetof(struct security_info, restrict_realtime),
},
{
.id = "RestrictSUIDSGID=",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictSUIDSGID=",
.description_good = "SUID/SGID file creation by service is restricted",
.description_bad = "Service may create SUID/SGID files",
.weight = 1000,
.range = 1,
.assess = assess_bool,
.offset = offsetof(struct security_info, restrict_suid_sgid),
},
{
.id = "RestrictNamespaces=~CLONE_NEWUSER",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
@ -1881,6 +1892,7 @@ static int acquire_security_info(sd_bus *bus, const char *name, struct security_
{ "RestrictAddressFamilies", "(bas)", property_read_restrict_address_families, 0 },
{ "RestrictNamespaces", "t", NULL, offsetof(struct security_info, restrict_namespaces) },
{ "RestrictRealtime", "b", NULL, offsetof(struct security_info, restrict_realtime) },
{ "RestrictSUIDSGID", "b", NULL, offsetof(struct security_info, restrict_suid_sgid) },
{ "RootDirectory", "s", NULL, offsetof(struct security_info, root_directory) },
{ "RootImage", "s", NULL, offsetof(struct security_info, root_image) },
{ "SupplementaryGroups", "as", NULL, offsetof(struct security_info, supplementary_groups) },

View File

@ -771,6 +771,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("ConfigurationDirectory", "as", NULL, offsetof(ExecContext, directories[EXEC_DIRECTORY_CONFIGURATION].paths), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("MemoryDenyWriteExecute", "b", bus_property_get_bool, offsetof(ExecContext, memory_deny_write_execute), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RestrictRealtime", "b", bus_property_get_bool, offsetof(ExecContext, restrict_realtime), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RestrictSUIDSGID", "b", bus_property_get_bool, offsetof(ExecContext, restrict_suid_sgid), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RestrictNamespaces", "t", bus_property_get_ulong, offsetof(ExecContext, restrict_namespaces), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("BindPaths", "a(ssbt)", property_get_bind_paths, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("BindReadOnlyPaths", "a(ssbt)", property_get_bind_paths, 0, SD_BUS_VTABLE_PROPERTY_CONST),
@ -1128,6 +1129,9 @@ int bus_exec_context_set_transient_property(
if (streq(name, "RestrictRealtime"))
return bus_set_transient_bool(u, name, &c->restrict_realtime, message, flags, error);
if (streq(name, "RestrictSUIDSGID"))
return bus_set_transient_bool(u, name, &c->restrict_suid_sgid, message, flags, error);
if (streq(name, "DynamicUser"))
return bus_set_transient_bool(u, name, &c->dynamic_user, message, flags, error);

View File

@ -1404,6 +1404,7 @@ static bool context_has_no_new_privileges(const ExecContext *c) {
return context_has_address_families(c) ||
c->memory_deny_write_execute ||
c->restrict_realtime ||
c->restrict_suid_sgid ||
exec_context_restrict_namespaces_set(c) ||
c->protect_kernel_tunables ||
c->protect_kernel_modules ||
@ -1509,6 +1510,19 @@ static int apply_restrict_realtime(const Unit* u, const ExecContext *c) {
return seccomp_restrict_realtime();
}
static int apply_restrict_suid_sgid(const Unit* u, const ExecContext *c) {
assert(u);
assert(c);
if (!c->restrict_suid_sgid)
return 0;
if (skip_seccomp_unavailable(u, "RestrictSUIDSGID="))
return 0;
return seccomp_restrict_suid_sgid();
}
static int apply_protect_sysctl(const Unit *u, const ExecContext *c) {
assert(u);
assert(c);
@ -3567,6 +3581,12 @@ static int exec_child(
return log_unit_error_errno(unit, r, "Failed to apply realtime restrictions: %m");
}
r = apply_restrict_suid_sgid(unit, context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
return log_unit_error_errno(unit, r, "Failed to apply SUID/SGID restrictions: %m");
}
r = apply_restrict_namespaces(unit, context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
@ -4218,6 +4238,7 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
"%sIgnoreSIGPIPE: %s\n"
"%sMemoryDenyWriteExecute: %s\n"
"%sRestrictRealtime: %s\n"
"%sRestrictSUIDSGID: %s\n"
"%sKeyringMode: %s\n"
"%sProtectHostname: %s\n",
prefix, c->umask,
@ -4237,6 +4258,7 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
prefix, yes_no(c->ignore_sigpipe),
prefix, yes_no(c->memory_deny_write_execute),
prefix, yes_no(c->restrict_realtime),
prefix, yes_no(c->restrict_suid_sgid),
prefix, exec_keyring_mode_to_string(c->keyring_mode),
prefix, yes_no(c->protect_hostname));

View File

@ -261,6 +261,7 @@ struct ExecContext {
bool memory_deny_write_execute;
bool restrict_realtime;
bool restrict_suid_sgid;
bool lock_personality;
unsigned long personality;

View File

@ -74,6 +74,7 @@ $1.SystemCallErrorNumber, config_parse_syscall_errno, 0,
$1.MemoryDenyWriteExecute, config_parse_bool, 0, offsetof($1, exec_context.memory_deny_write_execute)
$1.RestrictNamespaces, config_parse_restrict_namespaces, 0, offsetof($1, exec_context)
$1.RestrictRealtime, config_parse_bool, 0, offsetof($1, exec_context.restrict_realtime)
$1.RestrictSUIDSGID, config_parse_bool, 0, offsetof($1, exec_context.restrict_suid_sgid)
$1.RestrictAddressFamilies, config_parse_address_families, 0, offsetof($1, exec_context)
$1.LockPersonality, config_parse_bool, 0, offsetof($1, exec_context.lock_personality)',
`$1.SystemCallFilter, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
@ -82,6 +83,7 @@ $1.SystemCallErrorNumber, config_parse_warn_compat, DISABLED_CO
$1.MemoryDenyWriteExecute, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
$1.RestrictNamespaces, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
$1.RestrictRealtime, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
$1.RestrictSUIDSGID, config_parse_warn_compat, DISABLED_CONFIGURATION 0
$1.RestrictAddressFamilies, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
$1.LockPersonality, config_parse_warn_compat, DISABLED_CONFIGURATION, 0')
$1.LimitCPU, config_parse_rlimit, RLIMIT_CPU, offsetof($1, exec_context.rlimit)

View File

@ -4088,14 +4088,20 @@ int unit_patch_contexts(Unit *u) {
return -ENOMEM;
}
/* If the dynamic user option is on, let's make sure that the unit can't leave its UID/GID
* around in the file system or on IPC objects. Hence enforce a strict sandbox. */
/* If the dynamic user option is on, let's make sure that the unit can't leave its
* UID/GID around in the file system or on IPC objects. Hence enforce a strict
* sandbox. */
ec->private_tmp = true;
ec->remove_ipc = true;
ec->protect_system = PROTECT_SYSTEM_STRICT;
if (ec->protect_home == PROTECT_HOME_NO)
ec->protect_home = PROTECT_HOME_READ_ONLY;
/* Make sure this service can neither benefit from SUID/SGID binaries nor create
* them. */
ec->no_new_privileges = true;
ec->restrict_suid_sgid = true;
}
}

View File

@ -746,12 +746,12 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
return bus_append_string(m, field, eq);
if (STR_IN_SET(field,
"IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate",
"PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
"PrivateMounts", "NoNewPrivileges", "SyslogLevelPrefix",
"MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC",
"ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups",
"MountAPIVFS", "CPUSchedulingResetOnFork", "LockPersonality", "ProtectHostname"))
"IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate", "PrivateTmp",
"PrivateDevices", "PrivateNetwork", "PrivateUsers", "PrivateMounts",
"NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute", "RestrictRealtime",
"DynamicUser", "RemoveIPC", "ProtectKernelTunables", "ProtectKernelModules",
"ProtectControlGroups", "MountAPIVFS", "CPUSchedulingResetOnFork", "LockPersonality",
"ProtectHostname", "RestrictSUIDSGID"))
return bus_append_parse_boolean(m, field, eq);

View File

@ -1,12 +1,14 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <errno.h>
#include <fcntl.h>
#include <linux/seccomp.h>
#include <seccomp.h>
#include <stddef.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include "af-list.h"
#include "alloc-util.h"
@ -1776,16 +1778,20 @@ int seccomp_protect_hostname(void) {
SCMP_ACT_ERRNO(EPERM),
SCMP_SYS(sethostname),
0);
if (r < 0)
if (r < 0) {
log_debug_errno(r, "Failed to add sethostname() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch));
continue;
}
r = seccomp_rule_add_exact(
seccomp,
SCMP_ACT_ERRNO(EPERM),
SCMP_SYS(setdomainname),
0);
if (r < 0)
if (r < 0) {
log_debug_errno(r, "Failed to add setdomainname() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch));
continue;
}
r = seccomp_load(seccomp);
if (IN_SET(r, -EPERM, -EACCES))
@ -1796,3 +1802,133 @@ int seccomp_protect_hostname(void) {
return 0;
}
int seccomp_restrict_suid_sgid(void) {
uint32_t arch;
int r;
SECCOMP_FOREACH_LOCAL_ARCH(arch) {
_cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL;
r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ALLOW);
if (r < 0)
return r;
/* Checks the mode_t parameter of the following system calls:
*
* chmod() + fchmod() + fchmodat()
* open() + creat() + openat()
* mkdir() + mkdirat()
* mknod() + mknodat()
*/
for (unsigned bit = 0; bit < 2; bit ++) {
/* Block S_ISUID in the first iteration, S_ISGID in the second */
mode_t m = bit == 0 ? S_ISUID : S_ISGID;
r = seccomp_rule_add_exact(
seccomp,
SCMP_ACT_ERRNO(EPERM),
SCMP_SYS(chmod),
1,
SCMP_A1(SCMP_CMP_MASKED_EQ, m, m));
if (r < 0)
break;
r = seccomp_rule_add_exact(
seccomp,
SCMP_ACT_ERRNO(EPERM),
SCMP_SYS(fchmod),
1,
SCMP_A1(SCMP_CMP_MASKED_EQ, m, m));
if (r < 0)
break;
r = seccomp_rule_add_exact(
seccomp,
SCMP_ACT_ERRNO(EPERM),
SCMP_SYS(fchmodat),
1,
SCMP_A2(SCMP_CMP_MASKED_EQ, m, m));
if (r < 0)
break;
r = seccomp_rule_add_exact(
seccomp,
SCMP_ACT_ERRNO(EPERM),
SCMP_SYS(mkdir),
1,
SCMP_A1(SCMP_CMP_MASKED_EQ, m, m));
if (r < 0)
break;
r = seccomp_rule_add_exact(
seccomp,
SCMP_ACT_ERRNO(EPERM),
SCMP_SYS(mkdirat),
1,
SCMP_A2(SCMP_CMP_MASKED_EQ, m, m));
if (r < 0)
break;
r = seccomp_rule_add_exact(
seccomp,
SCMP_ACT_ERRNO(EPERM),
SCMP_SYS(mknod),
1,
SCMP_A1(SCMP_CMP_MASKED_EQ, m, m));
if (r < 0)
break;
r = seccomp_rule_add_exact(
seccomp,
SCMP_ACT_ERRNO(EPERM),
SCMP_SYS(mknodat),
1,
SCMP_A2(SCMP_CMP_MASKED_EQ, m, m));
if (r < 0)
break;
r = seccomp_rule_add_exact(
seccomp,
SCMP_ACT_ERRNO(EPERM),
SCMP_SYS(open),
2,
SCMP_A1(SCMP_CMP_MASKED_EQ, O_CREAT, O_CREAT),
SCMP_A2(SCMP_CMP_MASKED_EQ, m, m));
if (r < 0)
break;
r = seccomp_rule_add_exact(
seccomp,
SCMP_ACT_ERRNO(EPERM),
SCMP_SYS(openat),
2,
SCMP_A2(SCMP_CMP_MASKED_EQ, O_CREAT, O_CREAT),
SCMP_A3(SCMP_CMP_MASKED_EQ, m, m));
if (r < 0)
break;
r = seccomp_rule_add_exact(
seccomp,
SCMP_ACT_ERRNO(EPERM),
SCMP_SYS(creat),
1,
SCMP_A1(SCMP_CMP_MASKED_EQ, m, m));
if (r < 0)
break;
}
if (r < 0) {
log_debug_errno(r, "Failed to add suid/sgid rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch));
continue;
}
r = seccomp_load(seccomp);
if (IN_SET(r, -EPERM, -EACCES))
return r;
if (r < 0)
log_debug_errno(r, "Failed to apply suid/sgid restrictions for architecture %s, skipping: %m", seccomp_arch_to_string(arch));
}
return 0;
}

View File

@ -86,6 +86,7 @@ int seccomp_restrict_realtime(void);
int seccomp_memory_deny_write_execute(void);
int seccomp_lock_personality(unsigned long personality);
int seccomp_protect_hostname(void);
int seccomp_restrict_suid_sgid(void);
extern const uint32_t seccomp_local_archs[];

View File

@ -19,10 +19,12 @@
#include "nulstr-util.h"
#include "process-util.h"
#include "raw-clone.h"
#include "rm-rf.h"
#include "seccomp-util.h"
#include "set.h"
#include "string-util.h"
#include "tests.h"
#include "tmpfile-util.h"
#include "virt.h"
#if SCMP_SYS(socket) < 0 || defined(__i386__) || defined(__s390x__) || defined(__s390__)
@ -759,6 +761,211 @@ static void test_lock_personality(void) {
assert_se(wait_for_terminate_and_check("lockpersonalityseccomp", pid, WAIT_LOG) == EXIT_SUCCESS);
}
static int real_open(const char *path, int flags, mode_t mode) {
/* glibc internally calls openat() when open() is requested. Let's hence define our own wrapper for
* testing purposes that calls the real syscall. */
return (int) syscall(SYS_open, path, flags, mode);
}
static void test_restrict_suid_sgid(void) {
pid_t pid;
log_info("/* %s */", __func__);
if (!is_seccomp_available()) {
log_notice("Seccomp not available, skipping %s", __func__);
return;
}
if (geteuid() != 0) {
log_notice("Not root, skipping %s", __func__);
return;
}
pid = fork();
assert_se(pid >= 0);
if (pid == 0) {
char path[] = "/tmp/suidsgidXXXXXX", dir[] = "/tmp/suidsgiddirXXXXXX";
int fd = -1, k = -1;
const char *z;
fd = mkostemp_safe(path);
assert_se(fd >= 0);
assert_se(mkdtemp(dir));
z = strjoina(dir, "/test");
assert_se(chmod(path, 0755 | S_ISUID) >= 0);
assert_se(chmod(path, 0755 | S_ISGID) >= 0);
assert_se(chmod(path, 0755 | S_ISGID | S_ISUID) >= 0);
assert_se(chmod(path, 0755) >= 0);
assert_se(fchmod(fd, 0755 | S_ISUID) >= 0);
assert_se(fchmod(fd, 0755 | S_ISGID) >= 0);
assert_se(fchmod(fd, 0755 | S_ISGID | S_ISUID) >= 0);
assert_se(fchmod(fd, 0755) >= 0);
assert_se(fchmodat(AT_FDCWD, path, 0755 | S_ISUID, 0) >= 0);
assert_se(fchmodat(AT_FDCWD, path, 0755 | S_ISGID, 0) >= 0);
assert_se(fchmodat(AT_FDCWD, path, 0755 | S_ISGID | S_ISUID, 0) >= 0);
assert_se(fchmodat(AT_FDCWD, path, 0755, 0) >= 0);
k = real_open(z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644 | S_ISUID);
k = safe_close(k);
assert_se(unlink(z) >= 0);
k = real_open(z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644 | S_ISGID);
k = safe_close(k);
assert_se(unlink(z) >= 0);
k = real_open(z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644 | S_ISUID | S_ISGID);
k = safe_close(k);
assert_se(unlink(z) >= 0);
k = real_open(z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644);
k = safe_close(k);
assert_se(unlink(z) >= 0);
k = creat(z, 0644 | S_ISUID);
k = safe_close(k);
assert_se(unlink(z) >= 0);
k = creat(z, 0644 | S_ISGID);
k = safe_close(k);
assert_se(unlink(z) >= 0);
k = creat(z, 0644 | S_ISUID | S_ISGID);
k = safe_close(k);
assert_se(unlink(z) >= 0);
k = creat(z, 0644);
k = safe_close(k);
assert_se(unlink(z) >= 0);
k = openat(AT_FDCWD, z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644 | S_ISUID);
k = safe_close(k);
assert_se(unlink(z) >= 0);
k = openat(AT_FDCWD, z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644 | S_ISGID);
k = safe_close(k);
assert_se(unlink(z) >= 0);
k = openat(AT_FDCWD, z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644 | S_ISUID | S_ISGID);
k = safe_close(k);
assert_se(unlink(z) >= 0);
k = openat(AT_FDCWD, z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644);
k = safe_close(k);
assert_se(unlink(z) >= 0);
assert_se(mkdir(z, 0755 | S_ISUID) >= 0);
assert_se(rmdir(z) >= 0);
assert_se(mkdir(z, 0755 | S_ISGID) >= 0);
assert_se(rmdir(z) >= 0);
assert_se(mkdir(z, 0755 | S_ISUID | S_ISGID) >= 0);
assert_se(rmdir(z) >= 0);
assert_se(mkdir(z, 0755) >= 0);
assert_se(rmdir(z) >= 0);
assert_se(mkdirat(AT_FDCWD, z, 0755 | S_ISUID) >= 0);
assert_se(rmdir(z) >= 0);
assert_se(mkdirat(AT_FDCWD, z, 0755 | S_ISGID) >= 0);
assert_se(rmdir(z) >= 0);
assert_se(mkdirat(AT_FDCWD, z, 0755 | S_ISUID | S_ISGID) >= 0);
assert_se(rmdir(z) >= 0);
assert_se(mkdirat(AT_FDCWD, z, 0755) >= 0);
assert_se(rmdir(z) >= 0);
assert_se(mknod(z, S_IFREG | 0755 | S_ISUID, 0) >= 0);
assert_se(unlink(z) >= 0);
assert_se(mknod(z, S_IFREG | 0755 | S_ISGID, 0) >= 0);
assert_se(unlink(z) >= 0);
assert_se(mknod(z, S_IFREG | 0755 | S_ISUID | S_ISGID, 0) >= 0);
assert_se(unlink(z) >= 0);
assert_se(mknod(z, S_IFREG | 0755, 0) >= 0);
assert_se(unlink(z) >= 0);
assert_se(mknodat(AT_FDCWD, z, S_IFREG | 0755 | S_ISUID, 0) >= 0);
assert_se(unlink(z) >= 0);
assert_se(mknodat(AT_FDCWD, z, S_IFREG | 0755 | S_ISGID, 0) >= 0);
assert_se(unlink(z) >= 0);
assert_se(mknodat(AT_FDCWD, z, S_IFREG | 0755 | S_ISUID | S_ISGID, 0) >= 0);
assert_se(unlink(z) >= 0);
assert_se(mknodat(AT_FDCWD, z, S_IFREG | 0755, 0) >= 0);
assert_se(unlink(z) >= 0);
assert_se(seccomp_restrict_suid_sgid() >= 0);
assert_se(chmod(path, 0775 | S_ISUID) < 0 && errno == EPERM);
assert_se(chmod(path, 0775 | S_ISGID) < 0 && errno == EPERM);
assert_se(chmod(path, 0775 | S_ISGID | S_ISUID) < 0 && errno == EPERM);
assert_se(chmod(path, 0775) >= 0);
assert_se(fchmod(fd, 0775 | S_ISUID) < 0 && errno == EPERM);
assert_se(fchmod(fd, 0775 | S_ISGID) < 0 && errno == EPERM);
assert_se(fchmod(fd, 0775 | S_ISGID | S_ISUID) < 0 && errno == EPERM);
assert_se(fchmod(fd, 0775) >= 0);
assert_se(fchmodat(AT_FDCWD, path, 0755 | S_ISUID, 0) < 0 && errno == EPERM);
assert_se(fchmodat(AT_FDCWD, path, 0755 | S_ISGID, 0) < 0 && errno == EPERM);
assert_se(fchmodat(AT_FDCWD, path, 0755 | S_ISGID | S_ISUID, 0) < 0 && errno == EPERM);
assert_se(fchmodat(AT_FDCWD, path, 0755, 0) >= 0);
assert_se(real_open(z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644 | S_ISUID) < 0 && errno == EPERM);
assert_se(real_open(z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644 | S_ISGID) < 0 && errno == EPERM);
assert_se(real_open(z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644 | S_ISUID | S_ISGID) < 0 && errno == EPERM);
k = real_open(z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644);
k = safe_close(k);
assert_se(unlink(z) >= 0);
assert_se(creat(z, 0644 | S_ISUID) < 0 && errno == EPERM);
assert_se(creat(z, 0644 | S_ISGID) < 0 && errno == EPERM);
assert_se(creat(z, 0644 | S_ISUID | S_ISGID) < 0 && errno == EPERM);
k = creat(z, 0644);
k = safe_close(k);
assert_se(unlink(z) >= 0);
assert_se(openat(AT_FDCWD, z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644 | S_ISUID) < 0 && errno == EPERM);
assert_se(openat(AT_FDCWD, z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644 | S_ISGID) < 0 && errno == EPERM);
assert_se(openat(AT_FDCWD, z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644 | S_ISUID | S_ISGID) < 0 && errno == EPERM);
k = openat(AT_FDCWD, z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644);
k = safe_close(k);
assert_se(unlink(z) >= 0);
assert_se(mkdir(z, 0755 | S_ISUID) < 0 && errno == EPERM);
assert_se(mkdir(z, 0755 | S_ISGID) < 0 && errno == EPERM);
assert_se(mkdir(z, 0755 | S_ISUID | S_ISGID) < 0 && errno == EPERM);
assert_se(mkdir(z, 0755) >= 0);
assert_se(rmdir(z) >= 0);
assert_se(mkdirat(AT_FDCWD, z, 0755 | S_ISUID) < 0 && errno == EPERM);
assert_se(mkdirat(AT_FDCWD, z, 0755 | S_ISGID) < 0 && errno == EPERM);
assert_se(mkdirat(AT_FDCWD, z, 0755 | S_ISUID | S_ISGID) < 0 && errno == EPERM);
assert_se(mkdirat(AT_FDCWD, z, 0755) >= 0);
assert_se(rmdir(z) >= 0);
assert_se(mknod(z, S_IFREG | 0755 | S_ISUID, 0) < 0 && errno == EPERM);
assert_se(mknod(z, S_IFREG | 0755 | S_ISGID, 0) < 0 && errno == EPERM);
assert_se(mknod(z, S_IFREG | 0755 | S_ISUID | S_ISGID, 0) < 0 && errno == EPERM);
assert_se(mknod(z, S_IFREG | 0755, 0) >= 0);
assert_se(unlink(z) >= 0);
assert_se(mknodat(AT_FDCWD, z, S_IFREG | 0755 | S_ISUID, 0) < 0 && errno == EPERM);
assert_se(mknodat(AT_FDCWD, z, S_IFREG | 0755 | S_ISGID, 0) < 0 && errno == EPERM);
assert_se(mknodat(AT_FDCWD, z, S_IFREG | 0755 | S_ISUID | S_ISGID, 0) < 0 && errno == EPERM);
assert_se(mknodat(AT_FDCWD, z, S_IFREG | 0755, 0) >= 0);
assert_se(unlink(z) >= 0);
assert_se(unlink(path) >= 0);
assert_se(rm_rf(dir, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
_exit(EXIT_SUCCESS);
}
assert_se(wait_for_terminate_and_check("suidsgidseccomp", pid, WAIT_LOG) == EXIT_SUCCESS);
}
int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG);
@ -776,6 +983,7 @@ int main(int argc, char *argv[]) {
test_restrict_archs();
test_load_syscall_filter_set_raw();
test_lock_personality();
test_restrict_suid_sgid();
return 0;
}

View File

@ -851,6 +851,7 @@ ReserveVT=
RestrictAddressFamilies=
RestrictNamespaces=
RestrictRealtime=
RestrictSUIDSGID=
RuntimeDirectory=
RuntimeDirectoryMode=
RuntimeDirectoryPreserve=

View File

@ -36,6 +36,7 @@ ProtectSystem=strict
RestrictAddressFamilies=AF_UNIX
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
RuntimeMaxSec=5min
StateDirectory=systemd/coredump
SystemCallArchitectures=native

View File

@ -32,6 +32,7 @@ ReadWritePaths=/etc
RestrictAddressFamilies=AF_UNIX
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
SystemCallArchitectures=native
SystemCallErrorNumber=EPERM
SystemCallFilter=@system-service sethostname

View File

@ -17,7 +17,6 @@ DynamicUser=yes
ExecStart=@rootlibexecdir@/systemd-journal-gatewayd
LockPersonality=yes
MemoryDenyWriteExecute=yes
NoNewPrivileges=yes
PrivateDevices=yes
PrivateNetwork=yes
ProtectControlGroups=yes

View File

@ -30,6 +30,7 @@ ProtectSystem=strict
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
SystemCallArchitectures=native
User=systemd-journal-remote
WatchdogSec=3min

View File

@ -18,7 +18,6 @@ DynamicUser=yes
ExecStart=@rootlibexecdir@/systemd-journal-upload --save-state
LockPersonality=yes
MemoryDenyWriteExecute=yes
NoNewPrivileges=yes
PrivateDevices=yes
ProtectControlGroups=yes
ProtectHome=yes

View File

@ -28,6 +28,7 @@ RestartSec=0
RestrictAddressFamilies=AF_UNIX AF_NETLINK
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
Sockets=systemd-journald.socket systemd-journald-dev-log.socket systemd-journald-audit.socket
StandardOutput=null
SystemCallArchitectures=native

View File

@ -33,6 +33,7 @@ ReadWritePaths=/etc
RestrictAddressFamilies=AF_UNIX
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
SystemCallArchitectures=native
SystemCallErrorNumber=EPERM
SystemCallFilter=@system-service

View File

@ -40,6 +40,7 @@ RestartSec=0
RestrictAddressFamilies=AF_UNIX AF_NETLINK
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
RuntimeDirectory=systemd/sessions systemd/seats systemd/users systemd/inhibit systemd/shutdown
RuntimeDirectoryPreserve=yes
SystemCallArchitectures=native

View File

@ -34,6 +34,7 @@ RestartSec=0
RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 AF_PACKET
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
RuntimeDirectory=systemd/netif
RuntimeDirectoryPreserve=yes
SystemCallArchitectures=native

View File

@ -38,6 +38,7 @@ RestartSec=0
RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
RuntimeDirectory=systemd/resolve
RuntimeDirectoryPreserve=yes
SystemCallArchitectures=native

View File

@ -31,6 +31,7 @@ ReadWritePaths=/etc
RestrictAddressFamilies=AF_UNIX
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
SystemCallArchitectures=native
SystemCallErrorNumber=EPERM
SystemCallFilter=@system-service @clock

View File

@ -38,6 +38,7 @@ RestartSec=0
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
RuntimeDirectory=systemd/timesync
StateDirectory=systemd/timesync
SystemCallArchitectures=native

View File

@ -28,8 +28,9 @@ TasksMax=infinity
PrivateMounts=yes
ProtectHostname=yes
MemoryDenyWriteExecute=yes
RestrictRealtime=yes
RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
RestrictRealtime=yes
RestrictSUIDSGID=yes
SystemCallFilter=@system-service @module @raw-io
SystemCallErrorNumber=EPERM
SystemCallArchitectures=native