Files
netbird/client/ssh/server/setid_libc_linux.go
T

109 lines
2.6 KiB
Go

//go:build pkcs11 && linux && !android && (amd64 || arm64)
package server
import (
"fmt"
"runtime"
"sync"
"syscall"
"github.com/ebitengine/purego"
)
// This build links libc through purego, whose fakecgo runtime marks the binary as cgo
// without registering the libc hooks a real cgo build provides. syscall's setuid family
// then panics in AllThreadsSyscall, so the calls go to libc directly: glibc applies them
// to every thread, and all Go threads here are created through pthread_create.
type libcSetID struct {
setgroups func(size uintptr, list *uint32) int32
setgid func(gid uint32) int32
setuid func(uid uint32) int32
setegid func(egid uint32) int32
seteuid func(euid uint32) int32
errnoLocation func() *int32
}
var (
libcSetIDOnce sync.Once
libcSetIDFns libcSetID
libcSetIDErr error
)
func loadLibcSetID() (*libcSetID, error) {
libcSetIDOnce.Do(func() {
syms := []struct {
fn any
name string
}{
{&libcSetIDFns.setgroups, "setgroups"},
{&libcSetIDFns.setgid, "setgid"},
{&libcSetIDFns.setuid, "setuid"},
{&libcSetIDFns.setegid, "setegid"},
{&libcSetIDFns.seteuid, "seteuid"},
{&libcSetIDFns.errnoLocation, "__errno_location"},
}
for _, s := range syms {
addr, err := purego.Dlsym(purego.RTLD_DEFAULT, s.name)
if err != nil {
libcSetIDErr = fmt.Errorf("resolve libc %s: %w", s.name, err)
return
}
purego.RegisterFunc(s.fn, addr)
}
})
if libcSetIDErr != nil {
return nil, libcSetIDErr
}
return &libcSetIDFns, nil
}
// libcCall runs call on a locked thread so errno is read from the thread that set it.
func libcCall(call func(*libcSetID) int32) error {
fns, err := loadLibcSetID()
if err != nil {
return err
}
runtime.LockOSThread()
defer runtime.UnlockOSThread()
if call(fns) == 0 {
return nil
}
if errno := *fns.errnoLocation(); errno != 0 {
return syscall.Errno(errno)
}
return syscall.EINVAL
}
func setgroups(gids []int) error {
list := make([]uint32, len(gids))
for i, g := range gids {
list[i] = uint32(g)
}
return libcCall(func(f *libcSetID) int32 {
if len(list) == 0 {
return f.setgroups(0, nil)
}
return f.setgroups(uintptr(len(list)), &list[0])
})
}
func setgid(gid int) error {
return libcCall(func(f *libcSetID) int32 { return f.setgid(uint32(gid)) })
}
func setuid(uid int) error {
return libcCall(func(f *libcSetID) int32 { return f.setuid(uint32(uid)) })
}
func setegid(egid int) error {
return libcCall(func(f *libcSetID) int32 { return f.setegid(uint32(egid)) })
}
func seteuid(euid int) error {
return libcCall(func(f *libcSetID) int32 { return f.seteuid(uint32(euid)) })
}