func setresuid(ruid, euid, suid int) error { eno := C.csetresuid(C.uid_t(ruid), C.uid_t(euid), C.uid_t(suid)) if eno != 0 { return syscall.Errno(eno) } return nil }
// Set owner of the group control files and the @c tasks file. This function // modifies only libcgroup internal cgroup structure, use // Cgroup.Create() afterwards to create the group with given owners. // @param cgroup // @param tasks_uid UID of the owner of group's @c tasks file. // @param tasks_gid GID of the owner of group's @c tasks file. // @param control_uid UID of the owner of group's control files (i.e. // parameters). // @param control_gid GID of the owner of group's control files (i.e. // parameters). func (cg Cgroup) SetUidGid(tasks_uid UID, tasks_gid GID, control_uid UID, control_gid GID) error { return _err(C.cgroup_set_uid_gid(cg.g, C.uid_t(tasks_uid), C.gid_t(tasks_gid), C.uid_t(control_uid), C.gid_t(control_gid))) }
/* SetUIDGID sets owner of the group control files and the @c tasks file. This function modifies only libcgroup internal cgroup structure, use Cgroup.Create() afterwards to create the group with given owners. @param cgroup @param tasksUID UID of the owner of group's @c tasks file. @param tasksGID GID of the owner of group's @c tasks file. @param controlUID UID of the owner of group's control files (i.e. parameters). @param controlGID GID of the owner of group's control files (i.e. parameters). */ func (cg Cgroup) SetUIDGID(tasksUID UID, tasksGID GID, controlUID UID, controlGID GID) error { return _err(C.cgroup_set_uid_gid(cg.g, C.uid_t(tasksUID), C.gid_t(tasksGID), C.uid_t(controlUID), C.gid_t(controlGID))) }
func setuid(uid int) error { eno := C.csetuid(C.uid_t(uid)) if eno != 0 { return syscall.Errno(eno) } return nil }
// // Chown wraps keyctl_chown(3) to change ownership of the key. // // See: http://man7.org/linux/man-pages/man3/keyctl_chown.3.html // func Chown(key KeySerial, uid uint, gid uint) error { _, err := C.keyctl_chown(C.key_serial_t(key), C.uid_t(uid), C.gid_t(gid)) if err != nil { return err.(syscall.Errno) } return nil }
func getGIDForUID(uid string) (int, error) { var x C.gid_t n, err := ParseUID(uid) if err != nil { return 0, err } uidn := C.uid_t(n) if C.de_gid_for_uid(uidn, &x) < 0 { return 0, fmt.Errorf("cannot get GID for UID: %d", n) } return int(x), nil }
// RunCommandStatus attachs a shell and runs the command within the container. // The process will wait for the command to finish and return the result of // waitpid(), i.e. the process' exit status. An error is returned only when // invocation of the command completely fails. func (c *Container) RunCommandStatus(args []string, options AttachOptions) (int, error) { if len(args) == 0 { return -1, ErrInsufficientNumberOfArguments } if err := c.makeSure(isRunning); err != nil { return -1, err } c.mu.Lock() defer c.mu.Unlock() cargs := makeNullTerminatedArgs(args) if cargs == nil { return -1, ErrAllocationFailed } defer freeNullTerminatedArgs(cargs, len(args)) cenv := makeNullTerminatedArgs(options.Env) if cenv == nil { return -1, ErrAllocationFailed } defer freeNullTerminatedArgs(cenv, len(options.Env)) cenvToKeep := makeNullTerminatedArgs(options.EnvToKeep) if cenvToKeep == nil { return -1, ErrAllocationFailed } defer freeNullTerminatedArgs(cenvToKeep, len(options.EnvToKeep)) cwd := C.CString(options.Cwd) defer C.free(unsafe.Pointer(cwd)) ret := int(C.go_lxc_attach_run_wait( c.container, C.bool(options.ClearEnv), C.int(options.Namespaces), C.long(options.Arch), C.uid_t(options.UID), C.gid_t(options.GID), C.int(options.StdinFd), C.int(options.StdoutFd), C.int(options.StderrFd), cwd, cenv, cenvToKeep, cargs, )) return ret, nil }
func (a *InoAttr) toCStat(o *C.struct_stat, timeout *C.double) { o.st_ino = C.__darwin_ino64_t(a.Ino) o.st_mode = C.mode_t(a.Mode) o.st_nlink = C.nlink_t(a.Nlink) o.st_size = C.off_t(a.Size) if a.Uid != nil { o.st_uid = C.uid_t(*a.Uid) } if a.Gid != nil { o.st_gid = C.gid_t(*a.Gid) } toCTime(&o.st_ctimespec, a.Ctim) toCTime(&o.st_mtimespec, a.Mtim) toCTime(&o.st_atimespec, a.Atim) if timeout != nil { (*timeout) = C.double(a.Timeout) } }
// AttachShell attaches a shell to the container. // It clears all environment variables before attaching. func (c *Container) AttachShell(options AttachOptions) error { if err := c.makeSure(isRunning); err != nil { return err } c.mu.Lock() defer c.mu.Unlock() cenv := makeNullTerminatedArgs(options.Env) if cenv == nil { return ErrAllocationFailed } defer freeNullTerminatedArgs(cenv, len(options.Env)) cenvToKeep := makeNullTerminatedArgs(options.EnvToKeep) if cenvToKeep == nil { return ErrAllocationFailed } defer freeNullTerminatedArgs(cenvToKeep, len(options.EnvToKeep)) cwd := C.CString(options.Cwd) defer C.free(unsafe.Pointer(cwd)) ret := int(C.go_lxc_attach(c.container, C.bool(options.ClearEnv), C.int(options.Namespaces), C.long(options.Arch), C.uid_t(options.UID), C.gid_t(options.GID), C.int(options.StdinFd), C.int(options.StdoutFd), C.int(options.StderrFd), cwd, cenv, cenvToKeep, )) if ret < 0 { return ErrAttachFailed } return nil }
func changeUser(username string) error { uid, gid, err := user.IDs(username) if err != nil { return util.Errorf("Could not retrieve uid/gid for %q: %s", username, err) } userCstring := C.CString(username) defer C.free(unsafe.Pointer(userCstring)) ret, err := C.initgroups(userCstring, C.int(gid)) if ret != 0 && err != nil { return util.Errorf("Could not initgroups for %q (primary gid %v): %s", username, gid, err) } ret, err = C.setgid(C.gid_t(gid)) if ret != 0 && err != nil { return util.Errorf("Could not setgid %v: %s", gid, err) } ret, err = C.setuid(C.uid_t(uid)) if ret != 0 && err != nil { return util.Errorf("Could not setuid %v: %s", uid, err) } return nil }
// Run is a simple wrapper around Start() then Wait() with a common err return. func (c *Cmd) Start() (err error) { // If the process has already started then we can not continue here. if c.Process != nil { return fmt.Errorf("goclone: already started.") } // Check that UserMap and GroupMap are not defined if NewUserNameSpace // is not true. switch { case c.NewUserNameSpace: case c.UserMap != nil: return fmt.Errorf("goclone: UserMap set but NewUserNameSpace is false.") case c.GroupMap != nil: return fmt.Errorf( "goclone: GroupMap set but NewUserNameSpace is false.") } // Check to see if user name spaces are being used when the support // for them does not exist in the kernel. switch { case C.goclone_user_namespace_enabled == 1: case c.NewUserNameSpace: fallthrough case c.UserNameSpace != "": return fmt.Errorf("goclone: The kernel lacks user namespace support.") } // This function will walk through all of the file descriptors closing them // and returning the error passed in (or an error generated during the // Close() call if necessary. fail := func(err error) error { err = closeDescriptors(c.closeAfterStart, err) err = closeDescriptors(c.closeAfterWait, err) return err } // Setup the file descriptors needed to actually perform the syscall // level clone. stdin, err := c.reader(c.Stdin) if err != nil { return fail(err) } stdout, err := c.writer(c.Stdout) if err != nil { return fail(err) } // As a special case we have to check that stdout and stderr are not the // same object. If they are then we should use the same file descriptor // object rather than duplicating a whole new one. var stderr *os.File if c.Stdout == c.Stderr { stderr = stdout } else { stderr, err = c.writer(c.Stderr) if err != nil { return fail(err) } } // Make the native object type that is necessary in order to pass the // data into the cgo side of the world. We do this via a mmaped chunk // of memory so that we don't have to fiddle with malloc or any form // of shared memory allocations. Mmap works purely in pages so everything // needs to be sized to fit within pages of memory. // // The format for this allocation looks like this: // [ C.goclone_cmd ] - This will be several pages, rounded up to the // next page size. // [ buffer ] - This is necessary to ensure that we operate on sizes // one page at a time. // [ page ] - This is a single page that will be memprotected in order to // prevent the stack in the C side from growing large enough // to cause memory corruption. // [ stack ] - This is a chunk of memory used as the stack for the cloned // process. // [ page ] - This is another protected page. // // If double forking this will also be added to the allocation: // [ stack 2 ] - The stack for the short lived middle process. // [ page ] - Another protected page. // Start by calculating how much data needs to be available in the mmap // allocation. dataSize := rawDataSize(c) // Adjust the dataSize value to be a multiple of pageSize. dataSize = ((dataSize - 1) | (pageSize - 1)) + 1 // Now calculate the total size of the allocation. size := dataSize + pageSize + stackSize + pageSize if c.DoubleFork { size += stackSize + pageSize } // Allocate the space and ensure it gets freed when the routine exits. m, err := mmapDataAlloc(size) if err != nil { return fail(err) } defer func() { err2 := m.free() if err == nil && err2 != nil { err = err2 } }() // This boolean value is set if the credentials should be set at some // point. Its off by default unless a user defined the Credentials field // in the SysProcAttr field. setCredentials := false uid := uint32(0) gid := uint32(0) groups := []uint32(nil) if c.SysProcAttr != nil && c.SysProcAttr.Credential != nil { setCredentials = true uid = c.SysProcAttr.Credential.Uid gid = c.SysProcAttr.Credential.Gid groups = c.SysProcAttr.Credential.Groups } // If the c.SysProcAttr value is set then save the signal. deathSignal := syscall.SIGCHLD if c.SysProcAttr != nil && c.SysProcAttr.Pdeathsig != 0 { deathSignal = c.SysProcAttr.Pdeathsig } // The chroot directory is set from c.SysProcAttr if defined. chrootDir := "" if c.SysProcAttr != nil { chrootDir = c.SysProcAttr.Chroot } // Make an array of file descriptors that will be used to setup the // file descriptors in the child process. files := make([]int, len(c.ExtraFiles)+3) files[0] = int(stdin.Fd()) files[1] = int(stdout.Fd()) files[2] = int(stderr.Fd()) for i, fd := range c.ExtraFiles { if fd != nil { files[i+3] = int(fd.Fd()) } else { files[i+3] = -1 } } // Populate the various data elements in the allocation. cmd := m.pushGocloneCmd() // Exec settings. cmd.path = m.pushString(c.Path) cmd.args = m.pushStringSlice(c.Args) cmd.env = m.pushStringSlice(c.Env) cmd.dir = m.pushString(c.Dir) cmd.chroot_dir = m.pushString(chrootDir) // Set hostname only for new UTS namespace if c.NewUTSNameSpace { cmd.hostname = m.pushString(c.Hostname) } else { cmd.hostname = m.pushString("") } // file descriptors. cmd.files = m.pushIntSlice(files) cmd.files_len = C.int(len(files)) // Credentials cmd.set_credentials = C.bool(setCredentials) cmd.uid = C.uid_t(uid) cmd.gid = C.gid_t(gid) cmd.groups = m.pushGidSlice(groups) cmd.groups_len = C.int(len(groups)) // Cgroups tasks files. cmd.cgroups_tasks_files = m.pushStringSlice(c.CgroupsTasksFiles) // Namespaces cmd.ipc_namespace = m.pushString(c.IPCNameSpace) cmd.mount_namespace = m.pushString(c.MountNameSpace) cmd.network_namespace = m.pushString(c.NetworkNameSpace) cmd.user_namespace = m.pushString(c.UserNameSpace) cmd.uts_namespace = m.pushString(c.UTSNameSpace) cmd.pid_namespace = m.pushString(c.PIDNameSpace) cmd.new_ipc_namespace = C.bool(c.NewIPCNameSpace) cmd.new_network_namespace = C.bool(c.NewNetworkNameSpace) cmd.new_pid_namespace = C.bool(c.NewPIDNameSpace) cmd.new_mount_namespace = C.bool(c.NewMountNameSpace) cmd.new_user_namespace = C.bool(c.NewUserNameSpace) cmd.new_uts_namespace = C.bool(c.NewUTSNameSpace) // See if we should mount a new /proc from the underlying process. if os.Getuid() == 0 && c.NewPIDNameSpace { cmd.mount_new_proc = C.bool(true) } else { cmd.mount_new_proc = C.bool(false) } // Various simple settings. cmd.double_fork = C.bool(c.DoubleFork) cmd.death_signal = C.int(deathSignal) // Allocate the stacks. if err = m.mprotect(); err != nil { return } cmd.stack = m.stack() if err = m.mprotect(); err != nil { return } // If double forking, allocate another stack and protect the page after it. if c.DoubleFork { cmd.df_stack = m.stack() if err = m.mprotect(); err != nil { return } } // Next we need to create a goclone_parent_data for use by the parent when // helpign with the child. var data C.goclone_parent_data // Setup the uid_map. This will be written to /proc/child/uid_map if // defined. var uid_map bytes.Buffer if c.UserMap == nil { data.uid_map = nil data.uid_map_length = 0 } else { for i := range c.UserMap { elm := &c.UserMap[i] fmt.Fprintf(&uid_map, "%d %d %d\n", elm.Inside, elm.Outside, elm.Length) } data.uid_map = (*C.char)(unsafe.Pointer(&(uid_map.Bytes()[0]))) data.uid_map_length = (C.int)(uid_map.Len()) } // Setup the gid_map. This will be written to /proc/child/gid_map if // defined. var gid_map bytes.Buffer if c.UserMap == nil { data.gid_map = nil data.gid_map_length = 0 } else { for i := range c.UserMap { elm := &c.UserMap[i] fmt.Fprintf(&gid_map, "%d %d %d\n", elm.Inside, elm.Outside, elm.Length) } b := gid_map.Bytes() data.gid_map = (*C.char)(unsafe.Pointer(&b[0])) data.gid_map_length = (C.int)(gid_map.Len()) } // Call the C function. pid, err := C.goclone(cmd, &data) // Close any file descriptors that are no longer needed. err = closeDescriptors(c.closeAfterStart, err) // Make the Process structure. This functions foot print returns an error // but on linux an error can never be returned. c.Process, _ = os.FindProcess(int(pid)) // Start all of the copy goroutines. for _, f := range c.copyRoutines { c.errWG.Add(1) go f() } c.copyRoutines = nil return }