aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/linux/std.go
blob: 4ea5b903b8d16a33ef813c6d8646d4f01f62ae3d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package linux

import (
	"errors"
	"io"
	"io/fs"
	"os"
	"os/exec"
	"os/user"
	"strconv"
	"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

	uidOnce sync.Once
	uidCopy map[int]struct {
		uid int
		err error
	}
	uidMu sync.RWMutex
}

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 }

const xdgRuntimeDir = "XDG_RUNTIME_DIR"

func (s *Std) Paths() Paths {
	s.pathsOnce.Do(func() { CopyPaths(s, &s.paths) })
	return s.paths
}

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
	}
}

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
}