1// +build linux,cgo,seccomp
2
3package integration
4
5import (
6	"strings"
7	"syscall"
8	"testing"
9
10	"github.com/opencontainers/runc/libcontainer"
11	"github.com/opencontainers/runc/libcontainer/configs"
12	libseccomp "github.com/seccomp/libseccomp-golang"
13)
14
15func TestSeccompDenyGetcwd(t *testing.T) {
16	if testing.Short() {
17		return
18	}
19
20	rootfs, err := newRootfs()
21	if err != nil {
22		t.Fatal(err)
23	}
24	defer remove(rootfs)
25
26	config := newTemplateConfig(rootfs)
27	config.Seccomp = &configs.Seccomp{
28		DefaultAction: configs.Allow,
29		Syscalls: []*configs.Syscall{
30			{
31				Name:   "getcwd",
32				Action: configs.Errno,
33			},
34		},
35	}
36
37	container, err := newContainer(config)
38	if err != nil {
39		t.Fatal(err)
40	}
41	defer container.Destroy()
42
43	buffers := newStdBuffers()
44	pwd := &libcontainer.Process{
45		Cwd:    "/",
46		Args:   []string{"pwd"},
47		Env:    standardEnvironment,
48		Stdin:  buffers.Stdin,
49		Stdout: buffers.Stdout,
50		Stderr: buffers.Stderr,
51	}
52
53	err = container.Start(pwd)
54	if err != nil {
55		t.Fatal(err)
56	}
57	ps, err := pwd.Wait()
58	if err == nil {
59		t.Fatal("Expecting error (negative return code); instead exited cleanly!")
60	}
61
62	var exitCode int
63	status := ps.Sys().(syscall.WaitStatus)
64	if status.Exited() {
65		exitCode = status.ExitStatus()
66	} else if status.Signaled() {
67		exitCode = -int(status.Signal())
68	} else {
69		t.Fatalf("Unrecognized exit reason!")
70	}
71
72	if exitCode == 0 {
73		t.Fatalf("Getcwd should fail with negative exit code, instead got %d!", exitCode)
74	}
75
76	expected := "pwd: getcwd: Operation not permitted"
77	actual := strings.Trim(buffers.Stderr.String(), "\n")
78	if actual != expected {
79		t.Fatalf("Expected output %s but got %s\n", expected, actual)
80	}
81}
82
83func TestSeccompPermitWriteConditional(t *testing.T) {
84	if testing.Short() {
85		return
86	}
87
88	rootfs, err := newRootfs()
89	if err != nil {
90		t.Fatal(err)
91	}
92	defer remove(rootfs)
93
94	config := newTemplateConfig(rootfs)
95	config.Seccomp = &configs.Seccomp{
96		DefaultAction: configs.Allow,
97		Syscalls: []*configs.Syscall{
98			{
99				Name:   "write",
100				Action: configs.Errno,
101				Args: []*configs.Arg{
102					{
103						Index: 0,
104						Value: 1,
105						Op:    configs.GreaterThan,
106					},
107				},
108			},
109		},
110	}
111
112	container, err := newContainer(config)
113	if err != nil {
114		t.Fatal(err)
115	}
116	defer container.Destroy()
117
118	buffers := newStdBuffers()
119	dmesg := &libcontainer.Process{
120		Cwd:    "/",
121		Args:   []string{"busybox", "ls", "/"},
122		Env:    standardEnvironment,
123		Stdin:  buffers.Stdin,
124		Stdout: buffers.Stdout,
125		Stderr: buffers.Stderr,
126	}
127
128	err = container.Start(dmesg)
129	if err != nil {
130		t.Fatal(err)
131	}
132	if _, err := dmesg.Wait(); err != nil {
133		t.Fatalf("%s: %s", err, buffers.Stderr)
134	}
135}
136
137func TestSeccompDenyWriteConditional(t *testing.T) {
138	if testing.Short() {
139		return
140	}
141
142	// Only test if library version is v2.2.1 or higher
143	// Conditional filtering will always error in v2.2.0 and lower
144	major, minor, micro := libseccomp.GetLibraryVersion()
145	if (major == 2 && minor < 2) || (major == 2 && minor == 2 && micro < 1) {
146		return
147	}
148
149	rootfs, err := newRootfs()
150	if err != nil {
151		t.Fatal(err)
152	}
153	defer remove(rootfs)
154
155	config := newTemplateConfig(rootfs)
156	config.Seccomp = &configs.Seccomp{
157		DefaultAction: configs.Allow,
158		Syscalls: []*configs.Syscall{
159			{
160				Name:   "write",
161				Action: configs.Errno,
162				Args: []*configs.Arg{
163					{
164						Index: 0,
165						Value: 1,
166						Op:    configs.GreaterThan,
167					},
168				},
169			},
170		},
171	}
172
173	container, err := newContainer(config)
174	if err != nil {
175		t.Fatal(err)
176	}
177	defer container.Destroy()
178
179	buffers := newStdBuffers()
180	dmesg := &libcontainer.Process{
181		Cwd:    "/",
182		Args:   []string{"busybox", "ls", "does_not_exist"},
183		Env:    standardEnvironment,
184		Stdin:  buffers.Stdin,
185		Stdout: buffers.Stdout,
186		Stderr: buffers.Stderr,
187	}
188
189	err = container.Start(dmesg)
190	if err != nil {
191		t.Fatal(err)
192	}
193
194	ps, err := dmesg.Wait()
195	if err == nil {
196		t.Fatal("Expecting negative return, instead got 0!")
197	}
198
199	var exitCode int
200	status := ps.Sys().(syscall.WaitStatus)
201	if status.Exited() {
202		exitCode = status.ExitStatus()
203	} else if status.Signaled() {
204		exitCode = -int(status.Signal())
205	} else {
206		t.Fatalf("Unrecognized exit reason!")
207	}
208
209	if exitCode == 0 {
210		t.Fatalf("Busybox should fail with negative exit code, instead got %d!", exitCode)
211	}
212
213	// We're denying write to stderr, so we expect an empty buffer
214	expected := ""
215	actual := strings.Trim(buffers.Stderr.String(), "\n")
216	if actual != expected {
217		t.Fatalf("Expected output %s but got %s\n", expected, actual)
218	}
219}
220