From 5a62c0daff82e8343d24f98e1761d27bf8015782 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Wed, 20 Mar 2019 19:00:28 +0100
Subject: [PATCH] seccomp: introduce seccomp_restrict_suid_sgid() for blocking
 chmod() for suid/sgid files

(cherry picked from commit 3c27973b13724ede05a06a5d346a569794cda433)
Related: #1687512
---
 src/shared/seccomp-util.c | 132 ++++++++++++++++++++++++++++++++++++++
 src/shared/seccomp-util.h |   1 +
 2 files changed, 133 insertions(+)

diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c
index 92910acf0e..fd46b9f88d 100644
--- a/src/shared/seccomp-util.c
+++ b/src/shared/seccomp-util.c
@@ -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"
@@ -1747,3 +1749,133 @@ int seccomp_lock_personality(unsigned long personality) {
 
         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;
+}
diff --git a/src/shared/seccomp-util.h b/src/shared/seccomp-util.h
index d8a36c4e21..602f092255 100644
--- a/src/shared/seccomp-util.h
+++ b/src/shared/seccomp-util.h
@@ -85,6 +85,7 @@ int seccomp_restrict_address_families(Set *address_families, bool whitelist);
 int seccomp_restrict_realtime(void);
 int seccomp_memory_deny_write_execute(void);
 int seccomp_lock_personality(unsigned long personality);
+int seccomp_restrict_suid_sgid(void);
 
 extern const uint32_t seccomp_local_archs[];
 
