1 // Copyright 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "sandboxed_api/sandbox2/policy.h"
16
17 #include <syscall.h>
18
19 #include <cerrno>
20 #include <cstdlib>
21 #include <memory>
22 #include <string>
23 #include <utility>
24 #include <vector>
25
26 #include "gmock/gmock.h"
27 #include "gtest/gtest.h"
28 #include "absl/strings/string_view.h"
29 #include "sandboxed_api/config.h"
30 #include "sandboxed_api/sandbox2/executor.h"
31 #include "sandboxed_api/sandbox2/policybuilder.h"
32 #include "sandboxed_api/sandbox2/result.h"
33 #include "sandboxed_api/sandbox2/sandbox2.h"
34 #include "sandboxed_api/sandbox2/util/bpf_helper.h"
35 #include "sandboxed_api/testing.h"
36 #include "sandboxed_api/util/status_matchers.h"
37
38 namespace sandbox2 {
39 namespace {
40
41 using ::sapi::CreateDefaultPermissiveTestPolicy;
42 using ::sapi::GetTestSourcePath;
43 using ::testing::Eq;
44
45 #ifdef SAPI_X86_64
46 // Test that 32-bit syscalls from 64-bit are disallowed.
TEST(PolicyTest,AMD64Syscall32PolicyAllowed)47 TEST(PolicyTest, AMD64Syscall32PolicyAllowed) {
48 SKIP_ANDROID;
49 const std::string path = GetTestSourcePath("sandbox2/testcases/policy");
50
51 std::vector<std::string> args = {path, "1"};
52
53 SAPI_ASSERT_OK_AND_ASSIGN(auto policy,
54 CreateDefaultPermissiveTestPolicy(path).TryBuild());
55 Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
56 auto result = s2.Run();
57
58 ASSERT_THAT(result.final_status(), Eq(Result::VIOLATION));
59 EXPECT_THAT(result.reason_code(), Eq(1)); // __NR_exit in 32-bit
60 EXPECT_THAT(result.GetSyscallArch(), Eq(sapi::cpu::kX86));
61 }
62
63 // Test that 32-bit syscalls from 64-bit for FS checks are disallowed.
TEST(PolicyTest,AMD64Syscall32FsAllowed)64 TEST(PolicyTest, AMD64Syscall32FsAllowed) {
65 SKIP_ANDROID;
66 const std::string path = GetTestSourcePath("sandbox2/testcases/policy");
67 std::vector<std::string> args = {path, "2"};
68
69 SAPI_ASSERT_OK_AND_ASSIGN(auto policy,
70 CreateDefaultPermissiveTestPolicy(path).TryBuild());
71 Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
72 auto result = s2.Run();
73
74 ASSERT_THAT(result.final_status(), Eq(Result::VIOLATION));
75 EXPECT_THAT(result.reason_code(),
76 Eq(33)); // __NR_access in 32-bit
77 EXPECT_THAT(result.GetSyscallArch(), Eq(sapi::cpu::kX86));
78 }
79 #endif
80
81 // Test that ptrace(2) is disallowed.
TEST(PolicyTest,PtraceDisallowed)82 TEST(PolicyTest, PtraceDisallowed) {
83 const std::string path = GetTestSourcePath("sandbox2/testcases/policy");
84 std::vector<std::string> args = {path, "3"};
85
86 SAPI_ASSERT_OK_AND_ASSIGN(auto policy,
87 CreateDefaultPermissiveTestPolicy(path).TryBuild());
88 Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
89 auto result = s2.Run();
90
91 ASSERT_THAT(result.final_status(), Eq(Result::VIOLATION));
92 EXPECT_THAT(result.reason_code(), Eq(__NR_ptrace));
93 }
94
95 // Test that clone(2) with flag CLONE_UNTRACED is disallowed.
TEST(PolicyTest,CloneUntracedDisallowed)96 TEST(PolicyTest, CloneUntracedDisallowed) {
97 const std::string path = GetTestSourcePath("sandbox2/testcases/policy");
98 std::vector<std::string> args = {path, "4"};
99 SAPI_ASSERT_OK_AND_ASSIGN(auto policy,
100 CreateDefaultPermissiveTestPolicy(path).TryBuild());
101 Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
102 auto result = s2.Run();
103
104 ASSERT_THAT(result.final_status(), Eq(Result::VIOLATION));
105 EXPECT_THAT(result.reason_code(), Eq(__NR_clone));
106 }
107
108 // Test that bpf(2) is disallowed.
TEST(PolicyTest,BpfDisallowed)109 TEST(PolicyTest, BpfDisallowed) {
110 const std::string path = GetTestSourcePath("sandbox2/testcases/policy");
111 std::vector<std::string> args = {path, "5"};
112 SAPI_ASSERT_OK_AND_ASSIGN(auto policy,
113 CreateDefaultPermissiveTestPolicy(path).TryBuild());
114 Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
115 auto result = s2.Run();
116
117 ASSERT_THAT(result.final_status(), Eq(Result::VIOLATION));
118 EXPECT_THAT(result.reason_code(), Eq(__NR_bpf));
119 }
120
121 // Test that ptrace/bpf can return EPERM.
TEST(PolicyTest,BpfPtracePermissionDenied)122 TEST(PolicyTest, BpfPtracePermissionDenied) {
123 const std::string path = GetTestSourcePath("sandbox2/testcases/policy");
124 std::vector<std::string> args = {path, "7"};
125
126 SAPI_ASSERT_OK_AND_ASSIGN(
127 auto policy, CreateDefaultPermissiveTestPolicy(path)
128 .BlockSyscallsWithErrno({__NR_ptrace, __NR_bpf}, EPERM)
129 .TryBuild());
130 Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
131 auto result = s2.Run();
132
133 // ptrace/bpf is not a violation due to explicit policy. EPERM is expected.
134 ASSERT_THAT(result.final_status(), Eq(Result::OK));
135 EXPECT_THAT(result.reason_code(), Eq(0));
136 }
137
TEST(PolicyTest,IsattyAllowed)138 TEST(PolicyTest, IsattyAllowed) {
139 SKIP_SANITIZERS;
140 PolicyBuilder builder;
141 if constexpr (sapi::host_os::IsAndroid()) {
142 builder.DisableNamespaces().AllowDynamicStartup();
143 }
144 builder.AllowStaticStartup()
145 .AllowExit()
146 .AllowRead()
147 .AllowWrite()
148 .AllowTCGETS()
149 .AllowLlvmCoverage();
150 const std::string path = GetTestSourcePath("sandbox2/testcases/policy");
151 std::vector<std::string> args = {path, "6"};
152 SAPI_ASSERT_OK_AND_ASSIGN(auto policy, builder.TryBuild());
153 Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
154 auto result = s2.Run();
155
156 ASSERT_THAT(result.final_status(), Eq(Result::OK));
157 }
158
MinimalTestcasePolicy(absl::string_view path="")159 std::unique_ptr<Policy> MinimalTestcasePolicy(absl::string_view path = "") {
160 PolicyBuilder builder;
161
162 if constexpr (sapi::host_os::IsAndroid()) {
163 builder.AllowDynamicStartup();
164 builder.DisableNamespaces();
165 }
166
167 builder.AllowStaticStartup().AllowExit().AllowLlvmCoverage();
168 return builder.BuildOrDie();
169 }
170
171 // Test that we can sandbox a minimal static binary returning 0.
172 // If this starts failing, it means something changed, maybe in the way we
173 // compile static binaries, and we need to update the policy just above.
TEST(MinimalTest,MinimalBinaryWorks)174 TEST(MinimalTest, MinimalBinaryWorks) {
175 SKIP_ANDROID;
176 SKIP_SANITIZERS;
177 const std::string path = GetTestSourcePath("sandbox2/testcases/minimal");
178 std::vector<std::string> args = {path};
179 Sandbox2 s2(std::make_unique<Executor>(path, args),
180 MinimalTestcasePolicy(path));
181 auto result = s2.Run();
182
183 ASSERT_THAT(result.final_status(), Eq(Result::OK));
184 EXPECT_THAT(result.reason_code(), Eq(EXIT_SUCCESS));
185 }
186
187 // Test that we can sandbox a minimal non-static binary returning 0.
TEST(MinimalTest,MinimalSharedBinaryWorks)188 TEST(MinimalTest, MinimalSharedBinaryWorks) {
189 SKIP_SANITIZERS;
190 const std::string path =
191 GetTestSourcePath("sandbox2/testcases/minimal_dynamic");
192 std::vector<std::string> args = {path};
193
194 PolicyBuilder builder;
195
196 if constexpr (sapi::host_os::IsAndroid()) {
197 builder.DisableNamespaces();
198 } else {
199 builder.AddLibrariesForBinary(path);
200 }
201
202 builder.AllowDynamicStartup().AllowExit().AllowLlvmCoverage();
203 auto policy = builder.BuildOrDie();
204
205 Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
206 auto result = s2.Run();
207
208 ASSERT_THAT(result.final_status(), Eq(Result::OK));
209 EXPECT_THAT(result.reason_code(), Eq(EXIT_SUCCESS));
210 }
211
212 // Test that the AllowSystemMalloc helper works as expected.
TEST(MallocTest,SystemMallocWorks)213 TEST(MallocTest, SystemMallocWorks) {
214 SKIP_SANITIZERS;
215 const std::string path =
216 GetTestSourcePath("sandbox2/testcases/malloc_system");
217 std::vector<std::string> args = {path};
218
219 PolicyBuilder builder;
220
221 if constexpr (sapi::host_os::IsAndroid()) {
222 builder.DisableNamespaces();
223 builder.AllowDynamicStartup();
224 builder.AllowSyscalls({
225 __NR_madvise,
226 });
227 }
228
229 builder.AllowStaticStartup()
230 .AllowSystemMalloc()
231 .AllowExit()
232 .AllowLlvmCoverage();
233 auto policy = builder.BuildOrDie();
234
235 Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
236 auto result = s2.Run();
237
238 ASSERT_THAT(result.final_status(), Eq(Result::OK));
239 EXPECT_THAT(result.reason_code(), Eq(EXIT_SUCCESS));
240 }
241
242 // Complicated test to see that AddPolicyOnSyscalls work as
243 // expected. Specifically a worrisome corner-case would be that the logic was
244 // almost correct, but that the jump targets were off slightly. This uses the
245 // AddPolicyOnSyscall multiple times in a row to make any miscalculation
246 // unlikely to pass this check.
TEST(MultipleSyscalls,AddPolicyOnSyscallsWorks)247 TEST(MultipleSyscalls, AddPolicyOnSyscallsWorks) {
248 SKIP_SANITIZERS_AND_COVERAGE;
249 const std::string path =
250 GetTestSourcePath("sandbox2/testcases/add_policy_on_syscalls");
251 std::vector<std::string> args = {path};
252
253 PolicyBuilder builder;
254 if constexpr (sapi::host_os::IsAndroid()) {
255 builder.DisableNamespaces();
256 builder.AllowDynamicStartup();
257 }
258
259 builder.AllowStaticStartup()
260 .AllowTcMalloc()
261 .AllowExit()
262 .AddPolicyOnSyscalls(
263 {
264 __NR_getuid,
265 __NR_getgid,
266 __NR_geteuid,
267 __NR_getegid,
268 #ifdef __NR_getuid32
269 __NR_getuid32,
270 #endif
271 #ifdef __NR_getgid32
272 __NR_getgid32,
273 #endif
274 #ifdef __NR_geteuid32
275 __NR_geteuid32,
276 #endif
277 #ifdef __NR_getegid32
278 __NR_getegid32,
279 #endif
280 },
281 {ALLOW})
282 .AddPolicyOnSyscalls(
283 {
284 __NR_getresuid,
285 __NR_getresgid,
286 #ifdef __NR_getresuid32
287 __NR_getresuid32,
288 #endif
289 #ifdef __NR_getresgid32
290 __NR_getresgid32,
291 #endif
292 },
293 {ERRNO(42)})
294 .AddPolicyOnSyscalls({__NR_write}, {ERRNO(43)})
295 .AddPolicyOnSyscall(__NR_umask, {DENY});
296 auto policy = builder.BuildOrDie();
297
298 Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
299 auto result = s2.Run();
300
301 ASSERT_THAT(result.final_status(), Eq(Result::VIOLATION));
302 EXPECT_THAT(result.reason_code(), Eq(__NR_umask));
303 }
304
305 } // namespace
306 } // namespace sandbox2
307