From 8b45281daa3a87b4b7a3248263cd0ba929d15596 Mon Sep 17 00:00:00 2001 From: Arseny Maslennikov Date: Sun, 15 Oct 2023 11:00:00 +0300 Subject: [PATCH] seccomp: also check the mode parameter of `fchmodat2(2)` 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 | 24 +++++++++++++++++++++++- src/test/test-seccomp.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index 1712f3eff36..bb970d52647 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -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), diff --git a/src/test/test-seccomp.c b/src/test/test-seccomp.c index ecf383f43c3..56c4b3fd872 100644 --- a/src/test/test-seccomp.c +++ b/src/test/test-seccomp.c @@ -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); -- 2.47.3