]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
seccomp: also check the mode parameter of `fchmodat2(2)` 29618/head
authorArseny Maslennikov <arseny@altlinux.org>
Sun, 15 Oct 2023 08:00:00 +0000 (11:00 +0300)
committerArseny Maslennikov <arseny@altlinux.org>
Thu, 19 Oct 2023 12:53:58 +0000 (15:53 +0300)
If there is no libseccomp support, just ban the entire syscall instead
so wrappers will fall back to older, supported syscalls.
Also reflect all of this in `test-seccomp.c`.

src/shared/seccomp-util.c
src/test/test-seccomp.c

index 1712f3eff36a921772d162e5d35c0e5944f636f9..bb970d526470236d273648f91ad8205f38d5b417 100644 (file)
@@ -2081,7 +2081,7 @@ int seccomp_protect_hostname(void) {
 static int seccomp_restrict_sxid(scmp_filter_ctx seccomp, mode_t m) {
         /* Checks the mode_t parameter of the following system calls:
          *
-         *       → chmod() + fchmod() + fchmodat()
+         *       → chmod() + fchmod() + fchmodat() + fchmodat2()
          *       → open() + creat() + openat()
          *       → mkdir() + mkdirat()
          *       → mknod() + mknodat()
@@ -2124,6 +2124,28 @@ static int seccomp_restrict_sxid(scmp_filter_ctx seccomp, mode_t m) {
         else
                 any = true;
 
+#if defined(__SNR_fchmodat2)
+        r = seccomp_rule_add_exact(
+                        seccomp,
+                        SCMP_ACT_ERRNO(EPERM),
+                        SCMP_SYS(fchmodat2),
+                        1,
+                        SCMP_A2(SCMP_CMP_MASKED_EQ, m, m));
+#else
+        /* It looks like this libseccomp does not know about fchmodat2().
+         * Pretend the fchmodat2() system call is not supported at all,
+         * regardless of the kernel version. */
+        r = seccomp_rule_add_exact(
+                        seccomp,
+                        SCMP_ACT_ERRNO(ENOSYS),
+                        __NR_fchmodat2,
+                        0);
+#endif
+        if (r < 0)
+                log_debug_errno(r, "Failed to add filter for fchmodat2: %m");
+        else
+                any = true;
+
         r = seccomp_rule_add_exact(
                         seccomp,
                         SCMP_ACT_ERRNO(EPERM),
index ecf383f43c3e25159d29baf2f3b94e28aab478a2..56c4b3fd872e89b335dc47900f12bd46b8cb13ca 100644 (file)
@@ -21,6 +21,7 @@
 #include "macro.h"
 #include "memory-util.h"
 #include "missing_sched.h"
+#include "missing_syscall_def.h"
 #include "nsflags.h"
 #include "nulstr-util.h"
 #include "process-util.h"
@@ -1006,6 +1007,23 @@ static int real_open(const char *path, int flags, mode_t mode) {
 #endif
 }
 
+static int try_fchmodat2(int dirfd, const char *path, int flags, mode_t mode) {
+        /* glibc does not provide a direct wrapper for fchmodat2(). Let's hence define our own wrapper for
+         * testing purposes that calls the real syscall, on architectures and in environments where
+         * SYS_fchmodat2 is defined. Otherwise, let's just fall back to the glibc fchmodat() call. */
+
+#if defined __NR_fchmodat2 && __NR_fchmodat2 >= 0
+        int r;
+        r = (int) syscall(__NR_fchmodat2, dirfd, path, flags, mode);
+        /* The syscall might still be unsupported by kernel or libseccomp. */
+        if (r < 0 && errno == ENOSYS)
+                return fchmodat(dirfd, path, flags, mode);
+        return r;
+#else
+        return fchmodat(dirfd, path, flags, mode);
+#endif
+}
+
 TEST(restrict_suid_sgid) {
         pid_t pid;
 
@@ -1047,6 +1065,11 @@ TEST(restrict_suid_sgid) {
                 assert_se(fchmodat(AT_FDCWD, path, 0755 | S_ISGID | S_ISUID, 0) >= 0);
                 assert_se(fchmodat(AT_FDCWD, path, 0755, 0) >= 0);
 
+                assert_se(try_fchmodat2(AT_FDCWD, path, 0755 | S_ISUID, 0) >= 0);
+                assert_se(try_fchmodat2(AT_FDCWD, path, 0755 | S_ISGID, 0) >= 0);
+                assert_se(try_fchmodat2(AT_FDCWD, path, 0755 | S_ISGID | S_ISUID, 0) >= 0);
+                assert_se(try_fchmodat2(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);
@@ -1148,6 +1171,11 @@ TEST(restrict_suid_sgid) {
                 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(try_fchmodat2(AT_FDCWD, path, 0755 | S_ISUID, 0) < 0 && errno == EPERM);
+                assert_se(try_fchmodat2(AT_FDCWD, path, 0755 | S_ISGID, 0) < 0 && errno == EPERM);
+                assert_se(try_fchmodat2(AT_FDCWD, path, 0755 | S_ISGID | S_ISUID, 0) < 0 && errno == EPERM);
+                assert_se(try_fchmodat2(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);