forked from security/hakurei
internal/sys: separate hsu uid cache
This begins the effort of the removal of the sys package. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
@@ -2,16 +2,13 @@ package sys
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/hst"
|
||||
@@ -23,13 +20,7 @@ import (
|
||||
type Std struct {
|
||||
paths hst.Paths
|
||||
pathsOnce sync.Once
|
||||
|
||||
uidOnce sync.Once
|
||||
uidCopy map[int]struct {
|
||||
uid int
|
||||
err error
|
||||
}
|
||||
uidMu sync.RWMutex
|
||||
Hsu
|
||||
}
|
||||
|
||||
func (s *Std) Getuid() int { return os.Getuid() }
|
||||
@@ -53,81 +44,27 @@ func (s *Std) Paths() hst.Paths {
|
||||
s.pathsOnce.Do(func() {
|
||||
if userid, err := GetUserID(s); err != nil {
|
||||
// TODO(ophestra): this duplicates code in cmd/hakurei/command.go, keep this up to date until removal
|
||||
if m, ok := container.GetErrorMessage(err); ok {
|
||||
if m != "\x00" {
|
||||
log.Print(m)
|
||||
}
|
||||
} else {
|
||||
log.Println("cannot obtain user id from hsu:", err)
|
||||
}
|
||||
hlog.BeforeExit()
|
||||
s.Exit(1)
|
||||
const fallback = "cannot obtain user id from hsu:"
|
||||
|
||||
// this indicates the error message has already reached stderr, outside the current process's control;
|
||||
// this is only reached when hsu fails for any reason, as a second error message following hsu is confusing
|
||||
if errors.Is(err, ErrHsuAccess) {
|
||||
hlog.Verbose("*"+fallback, err)
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
|
||||
m, ok := container.GetErrorMessage(err)
|
||||
if !ok {
|
||||
log.Fatalln(fallback, err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Fatal(m)
|
||||
} else {
|
||||
CopyPaths(s, &s.paths, userid)
|
||||
}
|
||||
})
|
||||
return s.paths
|
||||
}
|
||||
|
||||
// this is a temporary placeholder until this package is removed
|
||||
type wrappedError struct {
|
||||
Err error
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (e *wrappedError) Error() string { return e.Err.Error() }
|
||||
func (e *wrappedError) Unwrap() error { return e.Err }
|
||||
func (e *wrappedError) Message() string { return e.Msg }
|
||||
|
||||
func (s *Std) Uid(identity int) (int, error) {
|
||||
s.uidOnce.Do(func() {
|
||||
s.uidCopy = make(map[int]struct {
|
||||
uid int
|
||||
err error
|
||||
})
|
||||
})
|
||||
|
||||
{
|
||||
s.uidMu.RLock()
|
||||
u, ok := s.uidCopy[identity]
|
||||
s.uidMu.RUnlock()
|
||||
if ok {
|
||||
return u.uid, u.err
|
||||
}
|
||||
}
|
||||
|
||||
s.uidMu.Lock()
|
||||
defer s.uidMu.Unlock()
|
||||
|
||||
u := struct {
|
||||
uid int
|
||||
err error
|
||||
}{}
|
||||
defer func() { s.uidCopy[identity] = u }()
|
||||
|
||||
u.uid = -1
|
||||
hsuPath := internal.MustHsuPath()
|
||||
|
||||
cmd := exec.Command(hsuPath)
|
||||
cmd.Path = hsuPath
|
||||
cmd.Stderr = os.Stderr // pass through fatal messages
|
||||
cmd.Env = []string{"HAKUREI_APP_ID=" + strconv.Itoa(identity)}
|
||||
cmd.Dir = container.FHSRoot
|
||||
var (
|
||||
p []byte
|
||||
exitError *exec.ExitError
|
||||
)
|
||||
|
||||
if p, u.err = cmd.Output(); u.err == nil {
|
||||
u.uid, u.err = strconv.Atoi(string(p))
|
||||
if u.err != nil {
|
||||
u.err = &wrappedError{u.err, "invalid uid string from hsu"}
|
||||
}
|
||||
} else if errors.As(u.err, &exitError) && exitError != nil && exitError.ExitCode() == 1 {
|
||||
// hsu prints an error message in this case
|
||||
u.err = &wrappedError{syscall.EACCES, "\x00"} // this drops the message, handled in cmd/hakurei/command.go
|
||||
} else if os.IsNotExist(u.err) {
|
||||
u.err = &wrappedError{os.ErrNotExist, fmt.Sprintf("the setuid helper is missing: %s", hsuPath)}
|
||||
}
|
||||
return u.uid, u.err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user