2024-11-02 03:03:44 +09:00
|
|
|
package linux
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2024-11-04 22:55:46 +09:00
|
|
|
"io"
|
2024-11-02 03:03:44 +09:00
|
|
|
"io/fs"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"os/user"
|
2024-11-16 21:19:45 +09:00
|
|
|
"strconv"
|
2024-11-02 03:03:44 +09:00
|
|
|
"sync"
|
|
|
|
|
|
|
|
"git.ophivana.moe/security/fortify/internal"
|
|
|
|
"git.ophivana.moe/security/fortify/internal/fmsg"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Std implements System using the standard library.
|
|
|
|
type Std struct {
|
|
|
|
paths Paths
|
|
|
|
pathsOnce sync.Once
|
|
|
|
|
|
|
|
sdBooted bool
|
|
|
|
sdBootedOnce sync.Once
|
|
|
|
|
2024-11-16 21:19:45 +09:00
|
|
|
uidOnce sync.Once
|
|
|
|
uidCopy map[int]struct {
|
|
|
|
uid int
|
|
|
|
err error
|
|
|
|
}
|
|
|
|
uidMu sync.RWMutex
|
2024-11-02 03:03:44 +09:00
|
|
|
}
|
|
|
|
|
2024-11-16 21:19:45 +09:00
|
|
|
func (s *Std) Geteuid() int { return os.Geteuid() }
|
|
|
|
func (s *Std) LookupEnv(key string) (string, bool) { return os.LookupEnv(key) }
|
|
|
|
func (s *Std) TempDir() string { return os.TempDir() }
|
|
|
|
func (s *Std) LookPath(file string) (string, error) { return exec.LookPath(file) }
|
|
|
|
func (s *Std) Executable() (string, error) { return os.Executable() }
|
|
|
|
func (s *Std) LookupGroup(name string) (*user.Group, error) { return user.LookupGroup(name) }
|
|
|
|
func (s *Std) ReadDir(name string) ([]os.DirEntry, error) { return os.ReadDir(name) }
|
|
|
|
func (s *Std) Stat(name string) (fs.FileInfo, error) { return os.Stat(name) }
|
|
|
|
func (s *Std) Open(name string) (fs.File, error) { return os.Open(name) }
|
|
|
|
func (s *Std) Exit(code int) { fmsg.Exit(code) }
|
|
|
|
func (s *Std) Stdout() io.Writer { return os.Stdout }
|
2024-11-02 03:03:44 +09:00
|
|
|
|
|
|
|
const xdgRuntimeDir = "XDG_RUNTIME_DIR"
|
|
|
|
|
|
|
|
func (s *Std) Paths() Paths {
|
|
|
|
s.pathsOnce.Do(func() { CopyPaths(s, &s.paths) })
|
|
|
|
return s.paths
|
|
|
|
}
|
|
|
|
|
2024-11-16 21:19:45 +09:00
|
|
|
func (s *Std) Uid(aid int) (int, error) {
|
|
|
|
s.uidOnce.Do(func() {
|
|
|
|
s.uidCopy = make(map[int]struct {
|
|
|
|
uid int
|
|
|
|
err error
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
s.uidMu.RLock()
|
|
|
|
if u, ok := s.uidCopy[aid]; ok {
|
|
|
|
s.uidMu.RUnlock()
|
|
|
|
return u.uid, u.err
|
|
|
|
}
|
|
|
|
|
|
|
|
s.uidMu.RUnlock()
|
|
|
|
s.uidMu.Lock()
|
|
|
|
defer s.uidMu.Unlock()
|
|
|
|
|
|
|
|
u := struct {
|
|
|
|
uid int
|
|
|
|
err error
|
|
|
|
}{}
|
|
|
|
defer func() { s.uidCopy[aid] = u }()
|
|
|
|
|
|
|
|
u.uid = -1
|
|
|
|
if fsu, ok := internal.Check(internal.Fsu); !ok {
|
|
|
|
fmsg.Fatal("invalid fsu path, this copy of fshim is not compiled correctly")
|
|
|
|
panic("unreachable")
|
|
|
|
} else {
|
|
|
|
cmd := exec.Command(fsu)
|
|
|
|
cmd.Path = fsu
|
|
|
|
cmd.Stderr = os.Stderr // pass through fatal messages
|
|
|
|
cmd.Env = []string{"FORTIFY_APP_ID=" + strconv.Itoa(aid)}
|
|
|
|
cmd.Dir = "/"
|
|
|
|
var p []byte
|
|
|
|
if p, u.err = cmd.Output(); u.err == nil {
|
|
|
|
u.uid, u.err = strconv.Atoi(string(p))
|
|
|
|
}
|
|
|
|
return u.uid, u.err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-02 03:03:44 +09:00
|
|
|
func (s *Std) SdBooted() bool {
|
|
|
|
s.sdBootedOnce.Do(func() { s.sdBooted = copySdBooted() })
|
|
|
|
return s.sdBooted
|
|
|
|
}
|
|
|
|
|
|
|
|
const systemdCheckPath = "/run/systemd/system"
|
|
|
|
|
|
|
|
func copySdBooted() bool {
|
|
|
|
if v, err := sdBooted(); err != nil {
|
|
|
|
fmsg.Println("cannot read systemd marker:", err)
|
|
|
|
return false
|
|
|
|
} else {
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func sdBooted() (bool, error) {
|
|
|
|
_, err := os.Stat(systemdCheckPath)
|
|
|
|
if err != nil {
|
|
|
|
if errors.Is(err, fs.ErrNotExist) {
|
|
|
|
err = nil
|
|
|
|
}
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return true, nil
|
|
|
|
}
|