]> Cypherpunks repositories - gostls13.git/commitdiff
syscall: support UID/GID map files for Linux user namespaces
authorMrunal Patel <mrunalp@gmail.com>
Thu, 2 Oct 2014 18:37:06 +0000 (11:37 -0700)
committerIan Lance Taylor <iant@golang.org>
Thu, 2 Oct 2014 18:37:06 +0000 (11:37 -0700)
Fixes #8447.

LGTM=iant
R=golang-codereviews, bradfitz, iant
CC=golang-codereviews
https://golang.org/cl/126190043

src/syscall/exec_linux.go

index f61dfc424a08a47a8621eac0059e36ca80c9eb43..72e2f0b69c82f64bf191515d6af4d5bc9d8949b6 100644 (file)
@@ -10,19 +10,29 @@ import (
        "unsafe"
 )
 
+// SysProcIDMap holds Container ID to Host ID mappings used for User Namespaces in Linux.
+// See user_namespaces(7).
+type SysProcIDMap struct {
+       ContainerID int // Container ID.
+       HostID      int // Host ID.
+       Size        int // Size.
+}
+
 type SysProcAttr struct {
-       Chroot     string      // Chroot.
-       Credential *Credential // Credential.
-       Ptrace     bool        // Enable tracing.
-       Setsid     bool        // Create session.
-       Setpgid    bool        // Set process group ID to new pid (SYSV setpgrp)
-       Setctty    bool        // Set controlling terminal to fd Ctty (only meaningful if Setsid is set)
-       Noctty     bool        // Detach fd 0 from controlling terminal
-       Ctty       int         // Controlling TTY fd (Linux only)
-       Pdeathsig  Signal      // Signal that the process will get when its parent dies (Linux only)
-       Cloneflags uintptr     // Flags for clone calls (Linux only)
-       Foreground bool        // Set foreground process group to child's pid. (Implies Setpgid. Stdin should be a TTY)
-       Joinpgrp   int         // If != 0, child's process group ID. (Setpgid must not be set)
+       Chroot      string         // Chroot.
+       Credential  *Credential    // Credential.
+       Ptrace      bool           // Enable tracing.
+       Setsid      bool           // Create session.
+       Setpgid     bool           // Set process group ID to new pid (SYSV setpgrp)
+       Setctty     bool           // Set controlling terminal to fd Ctty (only meaningful if Setsid is set)
+       Noctty      bool           // Detach fd 0 from controlling terminal
+       Ctty        int            // Controlling TTY fd (Linux only)
+       Pdeathsig   Signal         // Signal that the process will get when its parent dies (Linux only)
+       Cloneflags  uintptr        // Flags for clone calls (Linux only)
+       Foreground  bool           // Set foreground process group to child's pid. (Implies Setpgid. Stdin should be a TTY)
+       Joinpgrp    int            // If != 0, child's process group ID. (Setpgid must not be set)
+       UidMappings []SysProcIDMap // User ID mappings for user namespaces.
+       GidMappings []SysProcIDMap // Group ID mappings for user namespaces.
 }
 
 // Implemented in runtime package.
@@ -44,8 +54,10 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
        var (
                r1     uintptr
                err1   Errno
+               err2   Errno
                nextfd int
                i      int
+               p      [2]int
        )
 
        // Guard against side effects of shuffling fds below.
@@ -61,6 +73,14 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
        }
        nextfd++
 
+       // Allocate another pipe for parent to child communication for
+       // synchronizing writing of User ID/Group ID mappings.
+       if sys.UidMappings != nil || sys.GidMappings != nil {
+               if err := forkExecPipe(p[:]); err != nil {
+                       return 0, err.(Errno)
+               }
+       }
+
        // About to call fork.
        // No more allocation or calls of non-assembly functions.
        runtime_BeforeFork()
@@ -75,6 +95,16 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
                runtime_AfterFork()
                pid = int(r1)
 
+               if sys.UidMappings != nil || sys.GidMappings != nil {
+                       Close(p[0])
+                       err := writeUidGidMappings(pid, sys)
+                       if err != nil {
+                               err2 = err.(Errno)
+                       }
+                       RawSyscall(SYS_WRITE, uintptr(p[1]), uintptr(unsafe.Pointer(&err2)), unsafe.Sizeof(err2))
+                       Close(p[1])
+               }
+
                if sys.Joinpgrp != 0 {
                        // Place the child in the specified process group.
                        RawSyscall(SYS_SETPGID, r1, uintptr(sys.Joinpgrp), 0)
@@ -93,6 +123,25 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
 
        // Fork succeeded, now in child.
 
+       // Wait for User ID/Group ID mappings to be written.
+       if sys.UidMappings != nil || sys.GidMappings != nil {
+               if _, _, err1 = RawSyscall(SYS_CLOSE, uintptr(p[1]), 0, 0); err1 != 0 {
+                       goto childerror
+               }
+               r1, _, err1 = RawSyscall(SYS_READ, uintptr(p[0]), uintptr(unsafe.Pointer(&err2)), unsafe.Sizeof(err2))
+               if err1 != 0 {
+                       goto childerror
+               }
+               if r1 != unsafe.Sizeof(err2) {
+                       err1 = EINVAL
+                       goto childerror
+               }
+               if err2 != 0 {
+                       err1 = err2
+                       goto childerror
+               }
+       }
+
        // Parent death signal
        if sys.Pdeathsig != 0 {
                _, _, err1 = RawSyscall6(SYS_PRCTL, PR_SET_PDEATHSIG, uintptr(sys.Pdeathsig), 0, 0, 0, 0)
@@ -296,3 +345,53 @@ func forkExecPipe(p []int) (err error) {
        }
        return
 }
+
+// writeIDMappings writes the user namespace User ID or Group ID mappings to the specified path.
+func writeIDMappings(path string, idMap []SysProcIDMap) error {
+       fd, err := Open(path, O_RDWR, 0)
+       if err != nil {
+               return err
+       }
+
+       data := ""
+       for _, im := range idMap {
+               data = data + itoa(im.ContainerID) + " " + itoa(im.HostID) + " " + itoa(im.Size) + "\n"
+       }
+
+       bytes, err := ByteSliceFromString(data)
+       if err != nil {
+               Close(fd)
+               return err
+       }
+
+       if _, err := Write(fd, bytes); err != nil {
+               Close(fd)
+               return err
+       }
+
+       if err := Close(fd); err != nil {
+               return err
+       }
+
+       return nil
+}
+
+// writeUidGidMappings writes User ID and Group ID mappings for user namespaces
+// for a process and it is called from the parent process.
+func writeUidGidMappings(pid int, sys *SysProcAttr) error {
+       if sys.UidMappings != nil {
+               uidf := "/proc/" + itoa(pid) + "/uid_map"
+               if err := writeIDMappings(uidf, sys.UidMappings); err != nil {
+                       return err
+               }
+       }
+
+       if sys.GidMappings != nil {
+               gidf := "/proc/" + itoa(pid) + "/gid_map"
+               if err := writeIDMappings(gidf, sys.GidMappings); err != nil {
+                       return err
+               }
+       }
+
+       return nil
+}