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