]> Cypherpunks repositories - gostls13.git/commitdiff
syscall: add all ambient capabilities into permitted and inheritable sets
authorAndrei Vagin <avagin@google.com>
Mon, 7 Jan 2019 18:18:42 +0000 (10:18 -0800)
committerIan Lance Taylor <iant@golang.org>
Fri, 8 Mar 2019 20:53:16 +0000 (20:53 +0000)
According to the prctl man page, each capability from the ambient set
must already be present in both  the  permitted  and  the  inheritable
sets  of the process.

exec_linux_test suggests configuring the capabilities in the parent
process. This doesn't look nice, because:
* Capabilities are a per-thread attribute, so we need to use
LockOSThread.
* Need to restore capabilities after creating a process.
* Doesn't work with user namespaces, because a process gets capabilities
when a namespace is created.

Fixes #23152

Change-Id: Iba23e530fc7b9f5182d602fe855f82218f354219
Reviewed-on: https://go-review.googlesource.com/c/go/+/156577
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
src/syscall/exec_linux.go
src/syscall/exec_linux_test.go

index 79c0d774222df8174d216cbdd95db1eed8974bd1..ec8f296bca953cfb4147141840e48e3151a38829 100644 (file)
@@ -94,6 +94,29 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
        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
@@ -122,6 +145,7 @@ func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, att
                err2   Errno
                nextfd int
                i      int
+               caps   caps
        )
 
        // Record parent PID so child can test if it has died.
@@ -286,11 +310,32 @@ func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, att
                }
        }
 
-       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
index ac5745bc80bca5f9dc41efdc06fdeb4045bf316b..dc16a9d9fe440fc1378e54fe56dba5512df38c12 100644 (file)
@@ -436,7 +436,7 @@ func TestUnshareMountNameSpaceChroot(t *testing.T) {
 
 type capHeader struct {
        version uint32
-       pid     int
+       pid     int32
 }
 
 type capData struct {
@@ -446,6 +446,7 @@ type capData struct {
 }
 
 const CAP_SYS_TIME = 25
+const CAP_SYSLOG = 34
 
 type caps struct {
        hdr  capHeader
@@ -506,15 +507,28 @@ func TestAmbientCapsHelper(*testing.T) {
                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
@@ -530,20 +544,6 @@ func TestAmbientCaps(t *testing.T) {
                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)
@@ -588,7 +588,29 @@ func TestAmbientCaps(t *testing.T) {
                        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())