aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/linux
diff options
context:
space:
mode:
Diffstat (limited to 'internal/linux')
-rw-r--r--internal/linux/interface.go69
-rw-r--r--internal/linux/std.go83
2 files changed, 152 insertions, 0 deletions
diff --git a/internal/linux/interface.go b/internal/linux/interface.go
new file mode 100644
index 00000000..cf984a32
--- /dev/null
+++ b/internal/linux/interface.go
@@ -0,0 +1,69 @@
+package linux
+
+import (
+ "io/fs"
+ "os/user"
+ "path"
+ "strconv"
+
+ "git.ophivana.moe/security/fortify/internal/fmsg"
+)
+
+// System provides safe access to operating system resources.
+type System interface {
+ // Geteuid provides [os.Geteuid].
+ Geteuid() int
+ // LookupEnv provides [os.LookupEnv].
+ LookupEnv(key string) (string, bool)
+ // TempDir provides [os.TempDir].
+ TempDir() string
+ // LookPath provides [exec.LookPath].
+ LookPath(file string) (string, error)
+ // Executable provides [os.Executable].
+ Executable() (string, error)
+ // Lookup provides [user.Lookup].
+ Lookup(username string) (*user.User, error)
+ // ReadDir provides [os.ReadDir].
+ ReadDir(name string) ([]fs.DirEntry, error)
+ // Stat provides [os.Stat].
+ Stat(name string) (fs.FileInfo, error)
+ // Open provides [os.Open]
+ Open(name string) (fs.File, error)
+ // Exit provides [os.Exit].
+ Exit(code int)
+
+ // FshimPath returns an absolute path to the fshim binary.
+ FshimPath() string
+ // Paths returns a populated [Paths] struct.
+ Paths() Paths
+ // SdBooted implements https://www.freedesktop.org/software/systemd/man/sd_booted.html
+ SdBooted() bool
+}
+
+// Paths contains environment dependent paths used by fortify.
+type Paths struct {
+ // path to shared directory e.g. /tmp/fortify.%d
+ SharePath string `json:"share_path"`
+ // XDG_RUNTIME_DIR value e.g. /run/user/%d
+ RuntimePath string `json:"runtime_path"`
+ // application runtime directory e.g. /run/user/%d/fortify
+ RunDirPath string `json:"run_dir_path"`
+}
+
+// CopyPaths is a generic implementation of [System.Paths].
+func CopyPaths(os System, v *Paths) {
+ v.SharePath = path.Join(os.TempDir(), "fortify."+strconv.Itoa(os.Geteuid()))
+
+ fmsg.VPrintf("process share directory at %q", v.SharePath)
+
+ if r, ok := os.LookupEnv(xdgRuntimeDir); !ok || r == "" || !path.IsAbs(r) {
+ // fall back to path in share since fortify has no hard XDG dependency
+ v.RunDirPath = path.Join(v.SharePath, "run")
+ v.RuntimePath = path.Join(v.RunDirPath, "compat")
+ } else {
+ v.RuntimePath = r
+ v.RunDirPath = path.Join(v.RuntimePath, "fortify")
+ }
+
+ fmsg.VPrintf("runtime directory at %q", v.RunDirPath)
+}
diff --git a/internal/linux/std.go b/internal/linux/std.go
new file mode 100644
index 00000000..704ba162
--- /dev/null
+++ b/internal/linux/std.go
@@ -0,0 +1,83 @@
+package linux
+
+import (
+ "errors"
+ "io/fs"
+ "os"
+ "os/exec"
+ "os/user"
+ "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
+
+ fshim string
+ fshimOnce sync.Once
+}
+
+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) Lookup(username string) (*user.User, error) { return user.Lookup(username) }
+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) }
+
+const xdgRuntimeDir = "XDG_RUNTIME_DIR"
+
+func (s *Std) FshimPath() string {
+ s.fshimOnce.Do(func() {
+ p, ok := internal.Path(internal.Fshim)
+ if !ok {
+ fmsg.Fatal("invalid fshim path, this copy of fortify is not compiled correctly")
+ }
+ s.fshim = p
+ })
+
+ return s.fshim
+}
+
+func (s *Std) Paths() Paths {
+ s.pathsOnce.Do(func() { CopyPaths(s, &s.paths) })
+ return s.paths
+}
+
+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
+}