aboutsummaryrefslogtreecommitdiffhomepage
path: root/helper/stub.go
blob: cfd4a3f098d62d2ece1592e166692f190c49031d (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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package helper

import (
	"flag"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"
	"syscall"

	"git.gensokyo.uk/security/fortify/helper/bwrap"
	"git.gensokyo.uk/security/fortify/helper/proc"
)

// InternalHelperStub is an internal function but exported because it is cross-package;
// it is part of the implementation of the helper stub.
func InternalHelperStub() {
	// this test mocks the helper process
	var ap, sp string
	if v, ok := os.LookupEnv(FortifyHelper); !ok {
		return
	} else {
		ap = v
	}
	if v, ok := os.LookupEnv(FortifyStatus); !ok {
		panic(FortifyStatus)
	} else {
		sp = v
	}

	if len(os.Args) > 3 && os.Args[3] == "bwrap" {
		bwrapStub()
	} else {
		genericStub(flagRestoreFiles(3, ap, sp))
	}

	os.Exit(0)
}

func newFile(fd int, name, p string) *os.File {
	present := false
	switch p {
	case "0":
	case "1":
		present = true
	default:
		panic(fmt.Sprintf("%s fd has unexpected presence value %q", name, p))
	}

	f := os.NewFile(uintptr(fd), name)
	if !present && f != nil {
		panic(fmt.Sprintf("%s fd set but not present", name))
	}
	if present && f == nil {
		panic(fmt.Sprintf("%s fd preset but unset", name))
	}

	return f
}

func flagRestoreFiles(offset int, ap, sp string) (argsFile, statFile *os.File) {
	argsFd := flag.Int("args", -1, "")
	statFd := flag.Int("fd", -1, "")
	_ = flag.CommandLine.Parse(os.Args[offset:])
	argsFile = newFile(*argsFd, "args", ap)
	statFile = newFile(*statFd, "stat", sp)
	return
}

func genericStub(argsFile, statFile *os.File) {
	if argsFile != nil {
		// this output is checked by parent
		if _, err := io.Copy(os.Stderr, argsFile); err != nil {
			panic("cannot read args: " + err.Error())
		}
	}

	// simulate status pipe behaviour
	if statFile != nil {
		if _, err := statFile.Write([]byte{'x'}); err != nil {
			panic("cannot write to status pipe: " + err.Error())
		}

		done := make(chan struct{})
		go func() {
			// wait for status pipe close
			var epoll int
			if fd, err := syscall.EpollCreate1(0); err != nil {
				panic("cannot open epoll fd: " + err.Error())
			} else {
				defer func() {
					if err = syscall.Close(fd); err != nil {
						panic("cannot close epoll fd: " + err.Error())
					}
				}()
				epoll = fd
			}
			if err := syscall.EpollCtl(epoll, syscall.EPOLL_CTL_ADD, int(statFile.Fd()), &syscall.EpollEvent{}); err != nil {
				panic("cannot add status pipe to epoll: " + err.Error())
			}
			events := make([]syscall.EpollEvent, 1)
			if _, err := syscall.EpollWait(epoll, events, -1); err != nil {
				panic("cannot poll status pipe: " + err.Error())
			}
			if events[0].Events != syscall.EPOLLERR {
				panic(strconv.Itoa(int(events[0].Events)))

			}
			close(done)
		}()
		<-done
	}
}

func bwrapStub() {
	// the bwrap launcher does not launch with a typical sync fd
	argsFile, _ := flagRestoreFiles(4, "1", "0")

	// test args pipe behaviour
	func() {
		got, want := new(strings.Builder), new(strings.Builder)
		if _, err := io.Copy(got, argsFile); err != nil {
			panic("cannot read bwrap args: " + err.Error())
		}

		// hardcoded bwrap configuration used by test
		sc := &bwrap.Config{
			Net:           true,
			Hostname:      "localhost",
			Chdir:         "/proc/nonexistent",
			Clearenv:      true,
			NewSession:    true,
			DieWithParent: true,
			AsInit:        true,
		}

		if _, err := MustNewCheckedArgs(sc.Args(nil, new(proc.ExtraFilesPre), new([]proc.File))).
			WriteTo(want); err != nil {
			panic("cannot read want: " + err.Error())
		}

		if got.String() != want.String() {
			panic("bad bwrap args\ngot: " + got.String() + "\nwant: " + want.String())
		}
	}()

	if err := syscall.Exec(
		flag.CommandLine.Args()[0],
		flag.CommandLine.Args(),
		os.Environ()); err != nil {
		panic("cannot start helper stub: " + err.Error())
	}
}