mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-25 13:59:08 +01:00
109 lines
2.6 KiB
Go
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)) })
|
|
}
|