return pid, 0
}
+const _LINUX_CAPABILITY_VERSION_3 = 0x20080522
+
+type capHeader struct {
+ version uint32
+ pid int32
+}
+
+type capData struct {
+ effective uint32
+ permitted uint32
+ inheritable uint32
+}
+type caps struct {
+ hdr capHeader
+ data [2]capData
+}
+
+// See CAP_TO_INDEX in linux/capability.h:
+func capToIndex(cap uintptr) uintptr { return cap >> 5 }
+
+// See CAP_TO_MASK in linux/capability.h:
+func capToMask(cap uintptr) uint32 { return 1 << uint(cap&31) }
+
// forkAndExecInChild1 implements the body of forkAndExecInChild up to
// the parent's post-fork path. This is a separate function so we can
// separate the child's and parent's stack frames if we're using
err2 Errno
nextfd int
i int
+ caps caps
)
// Record parent PID so child can test if it has died.
}
}
- for _, c := range sys.AmbientCaps {
- _, _, err1 = RawSyscall6(SYS_PRCTL, PR_CAP_AMBIENT, uintptr(PR_CAP_AMBIENT_RAISE), c, 0, 0, 0)
- if err1 != 0 {
+ if len(sys.AmbientCaps) != 0 {
+ // Ambient capabilities were added in the 4.3 kernel,
+ // so it is safe to always use _LINUX_CAPABILITY_VERSION_3.
+ caps.hdr.version = _LINUX_CAPABILITY_VERSION_3
+
+ if _, _, err1 := RawSyscall(SYS_CAPGET, uintptr(unsafe.Pointer(&caps.hdr)), uintptr(unsafe.Pointer(&caps.data[0])), 0); err1 != 0 {
goto childerror
}
+
+ for _, c := range sys.AmbientCaps {
+ // Add the c capability to the permitted and inheritable capability mask,
+ // otherwise we will not be able to add it to the ambient capability mask.
+ caps.data[capToIndex(c)].permitted |= capToMask(c)
+ caps.data[capToIndex(c)].inheritable |= capToMask(c)
+ }
+
+ if _, _, err1 := RawSyscall(SYS_CAPSET, uintptr(unsafe.Pointer(&caps.hdr)), uintptr(unsafe.Pointer(&caps.data[0])), 0); err1 != 0 {
+ goto childerror
+ }
+
+ for _, c := range sys.AmbientCaps {
+ _, _, err1 = RawSyscall6(SYS_PRCTL, PR_CAP_AMBIENT, uintptr(PR_CAP_AMBIENT_RAISE), c, 0, 0, 0)
+ if err1 != 0 {
+ goto childerror
+ }
+ }
}
// Chdir
type capHeader struct {
version uint32
- pid int
+ pid int32
}
type capData struct {
}
const CAP_SYS_TIME = 25
+const CAP_SYSLOG = 34
type caps struct {
hdr capHeader
fmt.Fprintln(os.Stderr, "CAP_SYS_TIME unexpectedly not in the effective capability mask")
os.Exit(2)
}
+ if caps.data[1].effective&(1<<uint(CAP_SYSLOG&31)) == 0 {
+ fmt.Fprintln(os.Stderr, "CAP_SYSLOG unexpectedly not in the effective capability mask")
+ os.Exit(2)
+ }
}
func TestAmbientCaps(t *testing.T) {
- skipInContainer(t)
// Make sure we are running as root so we have permissions to use unshare
// and create a network namespace.
if os.Getuid() != 0 {
t.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace")
}
+
+ testAmbientCaps(t, false)
+}
+
+func TestAmbientCapsUserns(t *testing.T) {
+ testAmbientCaps(t, true)
+}
+
+func testAmbientCaps(t *testing.T, userns bool) {
+ skipInContainer(t)
mustSupportAmbientCaps(t)
// When running under the Go continuous build, skip tests for
t.Skip("skipping test on android; see Issue 27327")
}
- caps, err := getCaps()
- if err != nil {
- t.Fatal(err)
- }
-
- // Add CAP_SYS_TIME to the permitted and inheritable capability mask,
- // otherwise we will not be able to add it to the ambient capability mask.
- caps.data[0].permitted |= 1 << uint(CAP_SYS_TIME)
- caps.data[0].inheritable |= 1 << uint(CAP_SYS_TIME)
-
- if _, _, errno := syscall.Syscall(syscall.SYS_CAPSET, uintptr(unsafe.Pointer(&caps.hdr)), uintptr(unsafe.Pointer(&caps.data[0])), 0); errno != 0 {
- t.Fatalf("SYS_CAPSET: %v", errno)
- }
-
u, err := user.Lookup("nobody")
if err != nil {
t.Fatal(err)
Uid: uint32(uid),
Gid: uint32(gid),
},
- AmbientCaps: []uintptr{CAP_SYS_TIME},
+ AmbientCaps: []uintptr{CAP_SYS_TIME, CAP_SYSLOG},
+ }
+ if userns {
+ cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWUSER
+ const nobody = 65534
+ uid := os.Getuid()
+ gid := os.Getgid()
+ cmd.SysProcAttr.UidMappings = []syscall.SysProcIDMap{{
+ ContainerID: int(nobody),
+ HostID: int(uid),
+ Size: int(1),
+ }}
+ cmd.SysProcAttr.GidMappings = []syscall.SysProcIDMap{{
+ ContainerID: int(nobody),
+ HostID: int(gid),
+ Size: int(1),
+ }}
+
+ // Set credentials to run as user and group nobody.
+ cmd.SysProcAttr.Credential = &syscall.Credential{
+ Uid: nobody,
+ Gid: nobody,
+ }
}
if err := cmd.Run(); err != nil {
t.Fatal(err.Error())