func setresgid(rgid, egid, sgid int) error { eno := C.csetresgid(C.gid_t(rgid), C.gid_t(egid), C.gid_t(sgid)) if eno != 0 { return syscall.Errno(eno) } return nil }
/* 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))) }
// 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))) }
func GetGroupList(u *user.User) ([]string, error) { var members []string gid, err := strconv.Atoi(u.Gid) if err != nil { return nil, err } nameC := C.CString(u.Username) defer C.free(unsafe.Pointer(nameC)) groupC := C.gid_t(gid) ngroupsC := C.int(0) C.mygetgrouplist(nameC, groupC, nil, &ngroupsC) ngroups := int(ngroupsC) groups := C.malloc(C.size_t(int(unsafe.Sizeof(groupC)) * ngroups)) defer C.free(groups) rv := C.mygetgrouplist(nameC, groupC, (*C.gid_t)(groups), &ngroupsC) if rv == -1 { return nil, fmt.Errorf("user: membership of %s in %s: %s", u.Username, u.Gid, syscall.Errno(rv)) } ngroups = int(ngroupsC) for i := 0; i < ngroups; i++ { gid := C.group_at(C.int(i), (*C.gid_t)(groups)) gidS := strconv.Itoa(int(gid)) group, err := LookupGroupID(gidS) if err != nil { return nil, err } members = append(members, group.Name) } return members, nil }
func listGroups(u *User) ([]string, error) { ug, err := strconv.Atoi(u.Gid) if err != nil { return nil, fmt.Errorf("user: list groups for %s: invalid gid %q", u.Username, u.Gid) } userGID := C.gid_t(ug) nameC := C.CString(u.Username) defer C.free(unsafe.Pointer(nameC)) n := C.int(256) gidsC := make([]C.gid_t, n) rv := getGroupList(nameC, userGID, &gidsC[0], &n) if rv == -1 { // More than initial buffer, but now n contains the correct size. const maxGroups = 2048 if n > maxGroups { return nil, fmt.Errorf("user: list groups for %s: member of more than %d groups", u.Username, maxGroups) } gidsC = make([]C.gid_t, n) rv := getGroupList(nameC, userGID, &gidsC[0], &n) if rv == -1 { return nil, fmt.Errorf("user: list groups for %s failed (changed groups?)", u.Username) } } gidsC = gidsC[:n] gids := make([]string, 0, n) for _, g := range gidsC[:n] { gids = append(gids, strconv.Itoa(int(g))) } return gids, nil }
//NewGroup returns a new Group given a gid //or an error if any occurred func NewGroup(gid uint32) (*Group, error) { var buf = new(*C.char) grp, err := C.gidtogrp(C.gid_t(gid), buf) defer C.free(unsafe.Pointer(*buf)) defer C.free(unsafe.Pointer(grp)) if err != nil { return nil, err } if grp == nil { return nil, fmt.Errorf("gid does not exist") } length := int(C.getMemLength(grp.gr_mem)) hdr := reflect.SliceHeader{ Data: uintptr(unsafe.Pointer(grp.gr_mem)), Len: length, Cap: length, } return &Group{ Name: C.GoString(grp.gr_name), Passwd: C.GoString(grp.gr_passwd), Gid: uint32(grp.gr_gid), Mem: *(*[]string)(unsafe.Pointer(&hdr)), }, nil }
// Using Reflect // does not work func passingGrpBufferWithReflect(dst *C.struct_group, dstbuf *C.char, dstbuflen C.int, errnop *C.int) { ourGid := 52 byteArr := C.GoBytes(unsafe.Pointer(buf), buflen) byteBuf := bytes.NewBuffer(byteArr[:0]) for _, s := range []string{"name", "passwd", "one", "two", "three"} { byteBuf.WriteString(s) byteBuf.WriteByte(0) } var p *C.char memPtr := []*C.char{ (*C.char)(unsafe.Pointer(&byteArr[12])), (*C.char)(unsafe.Pointer(&byteArr[16])), (*C.char)(unsafe.Pointer(&byteArr[20])), (*C.char)(nil), } lenCap := len(memPtr) * int(unsafe.Sizeof(p)) refSlice := &reflect.SliceHeader{ Data: uintptr(unsafe.Pointer(&memPtr[0])), Len: lenCap, Cap: lenCap} memBytes := *(*[]byte)(unsafe.Pointer(refSlice)) byteBuf.Write(memBytes) grp.gr_name = (*C.char)(unsafe.Pointer(&byteArr[0])) grp.gr_passwd = (*C.char)(unsafe.Pointer(&byteArr[5])) grp.gr_gid = C.gid_t(ourGid) grp.gr_mem = (**C.char)(unsafe.Pointer(&byteArr[26])) }
func setgid(gid int) error { eno := C.csetgid(C.gid_t(gid)) if eno != 0 { return syscall.Errno(eno) } return nil }
func userInGroup(u *user.User, g *Group) (bool, error) { if u.Gid == g.Gid { return true, nil } gid, err := strconv.Atoi(g.Gid) if err != nil { return false, err } nameC := C.CString(u.Username) defer C.free(unsafe.Pointer(nameC)) groupC := C.gid_t(gid) ngroupsC := C.int(0) C.mygetgrouplist(nameC, groupC, nil, &ngroupsC) ngroups := int(ngroupsC) groups := C.malloc(C.size_t(int(unsafe.Sizeof(groupC)) * ngroups)) defer C.free(groups) rv := C.mygetgrouplist(nameC, groupC, (*C.gid_t)(groups), &ngroupsC) if rv == -1 { return false, fmt.Errorf("user: membership of %s in %s: %s", u.Username, g.Name, syscall.Errno(rv)) } ngroups = int(ngroupsC) for i := 0; i < ngroups; i++ { gid := C.group_at(C.int(i), (*C.gid_t)(groups)) if g.Gid == strconv.Itoa(int(gid)) { return true, nil } } return false, nil }
// Because Go is like... naaaaa, no groups aren't a thing! // Based on Go's src/os/user/lookup_unix.go func currentUserAndGroup() (*userAndGroup, error) { u, err := user.Current() if err != nil { return nil, err } gid, err := strconv.Atoi(u.Gid) if err != nil { return nil, err } var grp C.struct_group var result *C.struct_group buflen := C.sysconf(C._SC_GETPW_R_SIZE_MAX) if buflen <= 0 || buflen > 1<<20 { return nil, fmt.Errorf("unreasonable _SC_GETGR_R_SIZE_MAX of %d", buflen) } buf := C.malloc(C.size_t(buflen)) defer C.free(buf) r := C.mygetgrgid_r(C.gid_t(gid), &grp, (*C.char)(buf), C.size_t(buflen), &result) if r != 0 { return nil, fmt.Errorf("lookup gid %d: %s", gid, syscall.Errno(r)) } if result == nil { return nil, fmt.Errorf("lookup gid %d failed", gid) } return &userAndGroup{ User: u, Groupname: C.GoString(grp.gr_name), }, 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 }
// Pushes an array of gid_t's into the mmapData buffer. func (m *mmapData) pushGidSlice(gs []uint32) *C.gid_t { ptr := unsafe.Pointer(&m.data[m.offset]) nextptr := ptr for _, g := range gs { *(*C.gid_t)(nextptr) = C.gid_t(g) m.offset += gidSize nextptr = unsafe.Pointer(&m.data[m.offset]) } m.panicIfOverAllocated() return (*C.gid_t)(ptr) }
func getExtraGIDs(gid int) (gids []int, err error) { gidn := C.gid_t(gid) f := func(gid C.gid_t) { gids = append(gids, int(gid)) } if C.de_get_extra_gids(gidn, C.uintptr_t(uintptr(unsafe.Pointer(&f)))) < 0 { return nil, fmt.Errorf("cannot retrieve additional groups list for GID %d", gid) } return }
// 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 lookup(gid int, groupname string, lookupByName bool) (*Group, error) { var grp C.struct_group var result *C.struct_group var bufsize C.long bufsize = C.sysconf(C._SC_GETGR_R_SIZE_MAX) if bufsize == -1 { bufsize = 1024 } buf := C.malloc(C.size_t(bufsize)) defer C.free(buf) var rv C.int if lookupByName { CGroup := C.CString(groupname) defer C.free(unsafe.Pointer(CGroup)) rv = C.getgrnam_r(CGroup, &grp, (*C.char)(buf), C.size_t(bufsize), &result) if rv != 0 { return nil, fmt.Errorf("group: lookup group name %s: %s", groupname, syscall.Errno(rv)) } if result == nil { return nil, UnknownGroupError(groupname) } } else { rv = C.getgrgid_r(C.gid_t(gid), &grp, (*C.char)(buf), C.size_t(bufsize), &result) if rv != 0 { return nil, fmt.Errorf("group: lookup gid %d: %s", gid, syscall.Errno(rv)) } if result == nil { return nil, UnknownGroupIdError(gid) } } g := &Group{ Gid: int(grp.gr_gid), Name: C.GoString(grp.gr_name), Members: convert(grp.gr_mem), } return g, 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 }
import "C" import ( "syscall" "unsafe" ) var ( // The size of a pointer in bytes in the system. ptrSize = int(unsafe.Sizeof(unsafe.Pointer(nil))) // The size of a C integer in bytes on the system. intSize = int(unsafe.Sizeof(C.int(0))) // The size of a gid_t type in bytes on the system. gidSize = int(unsafe.Sizeof(C.gid_t(0))) // The size of system pages on the system. pageSize = syscall.Getpagesize() // The size that should be allocated for stacks. stackSize = pageSize * 8 ) // This function call will calculate exactly how much space is expected to be // used by the data elements of the c array. The returned size here will // not be page size shifted. func rawDataSize(c *Cmd) int { // First calculate the size of the C.goclone_cmd, then add each of the // strings, or arrays to the size so we know how much space to allocate. dataSize := int(unsafe.Sizeof(C.goclone_cmd{}))