Skip to content

Commit 3b9b58b

Browse files
kolyshkingopherbot
authored andcommitted
unix: Faccess: check CAP_DAC_OVERRIDE on Linux
CL 126516 added support for flags argument, implemented in the same way as glibc does (it tries to guess what the kernel would do). CL 246537 added using faccess2(2) Linux syscall which supports the flags directly. For older kernels, though, the syscall is not available, and the code uses glibc-like fallback. There is one very specific scenario in which the fallback fails. The scenario involves all these conditions: - no faccessat2 support available (i.e. either Linux kernel < 5.8, or a seccomp set up to disable faccessat2); - the current user is not root (i.e. geteuid() != 0); - CAP_DAC_OVERRIDE capability is set for the current process; - the file to be executed does not have executable permission bit set for either the current EUID or EGID; - the file to be executed have at least one executable bit set. Unfortunately, this set of conditions was observed in the wild -- a container run as a non-root user with the binary file owned by root with executable permission set for a user only [1]. Essentially it means it is not as rare as it may seem. Now, CAP_DAC_OVERRIDE essentially makes the kernel bypass most of the checks, so execve(2) and friends work the same was as for root user, i.e. if at least one executable bit it set, the permission to execute is granted (see generic_permission() function in the Linux kernel). Modify the code to check for CAP_DAC_OVERRIDE and mimic the kernel behavior for permission checks. This is essentially the same fix as CL 468735 for Go syscall package. Tested on CentOS 7 with the repro similar to the one from [2]. [1] opencontainers/runc#3715 [2] golang/go#58552 (comment) Change-Id: I726b6acab6a6e6d0358ef98e6a582b405c347614 Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com> Reviewed-on: https://go-review.googlesource.com/c/sys/+/468877 Reviewed-by: Ian Lance Taylor <iant@google.com> Run-TryBot: Ian Lance Taylor <iant@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Bryan Mills <bcmills@google.com> Auto-Submit: Ian Lance Taylor <iant@google.com>
1 parent 2da1413 commit 3b9b58b

File tree

1 file changed

+14
-0
lines changed

1 file changed

+14
-0
lines changed

unix/syscall_linux.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2154,6 +2154,14 @@ func isGroupMember(gid int) bool {
21542154
return false
21552155
}
21562156

2157+
func isCapDacOverrideSet() bool {
2158+
hdr := CapUserHeader{Version: LINUX_CAPABILITY_VERSION_3}
2159+
data := [2]CapUserData{}
2160+
err := Capget(&hdr, &data[0])
2161+
2162+
return err == nil && data[0].Effective&(1<<CAP_DAC_OVERRIDE) != 0
2163+
}
2164+
21572165
//sys faccessat(dirfd int, path string, mode uint32) (err error)
21582166
//sys Faccessat2(dirfd int, path string, mode uint32, flags int) (err error)
21592167

@@ -2189,6 +2197,12 @@ func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) {
21892197
var uid int
21902198
if flags&AT_EACCESS != 0 {
21912199
uid = Geteuid()
2200+
if uid != 0 && isCapDacOverrideSet() {
2201+
// If CAP_DAC_OVERRIDE is set, file access check is
2202+
// done by the kernel in the same way as for root
2203+
// (see generic_permission() in the Linux sources).
2204+
uid = 0
2205+
}
21922206
} else {
21932207
uid = Getuid()
21942208
}

0 commit comments

Comments
 (0)