Skip to content

Commit aef8a8c

Browse files
kolyshkingopherbot
authored andcommitted
[release-branch.go1.20] syscall: Faccessat: check for CAP_DAC_OVERRIDE on Linux
CL 416115 added using faccessat2(2) from syscall.Faccessat on Linux (which is the only true way to implement AT_EACCESS flag handing), if available. If not available, it uses some heuristics to mimic the kernel behavior, mostly taken from glibc (see CL 126415). Next, CL 414824 added using the above call (via unix.Eaccess) to exec.LookPath in order to check if the binary can really be executed. As a result, in a very specific scenario, described below, syscall.Faccessat (and thus exec.LookPath) mistakenly tells that the binary can not be executed, while in reality it can be. This makes this bug a regression in Go 1.20. This 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. [1] opencontainers/runc#3715 For #58552. Fixes #58624. Change-Id: I82a7e757ab3fd3d0193690a65c3b48fee46ff067 Reviewed-on: https://go-review.googlesource.com/c/go/+/468735 Reviewed-by: Damien Neil <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> Auto-Submit: Ian Lance Taylor <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> (cherry picked from commit 031401a) Reviewed-on: https://go-review.googlesource.com/c/go/+/469956 Auto-Submit: Dmitri Shuralyov <[email protected]> Run-TryBot: Than McIntosh <[email protected]> Reviewed-by: Than McIntosh <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]>
1 parent ef79380 commit aef8a8c

File tree

1 file changed

+17
-0
lines changed

1 file changed

+17
-0
lines changed

src/syscall/syscall_linux.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,16 @@ func isGroupMember(gid int) bool {
138138
return false
139139
}
140140

141+
func isCapDacOverrideSet() bool {
142+
const _CAP_DAC_OVERRIDE = 1
143+
var c caps
144+
c.hdr.version = _LINUX_CAPABILITY_VERSION_3
145+
146+
_, _, err := RawSyscall(SYS_CAPGET, uintptr(unsafe.Pointer(&c.hdr)), uintptr(unsafe.Pointer(&c.data[0])), 0)
147+
148+
return err == 0 && c.data[0].effective&capToMask(_CAP_DAC_OVERRIDE) != 0
149+
}
150+
141151
//sys faccessat(dirfd int, path string, mode uint32) (err error)
142152
//sys faccessat2(dirfd int, path string, mode uint32, flags int) (err error) = _SYS_faccessat2
143153

@@ -179,9 +189,16 @@ func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) {
179189
return nil
180190
}
181191

192+
// Fallback to checking permission bits.
182193
var uid int
183194
if flags&_AT_EACCESS != 0 {
184195
uid = Geteuid()
196+
if uid != 0 && isCapDacOverrideSet() {
197+
// If CAP_DAC_OVERRIDE is set, file access check is
198+
// done by the kernel in the same way as for root
199+
// (see generic_permission() in the Linux sources).
200+
uid = 0
201+
}
185202
} else {
186203
uid = Getuid()
187204
}

0 commit comments

Comments
 (0)