1*ec63e07aSXin Li // Copyright 2019 Google LLC
2*ec63e07aSXin Li //
3*ec63e07aSXin Li // Licensed under the Apache License, Version 2.0 (the "License");
4*ec63e07aSXin Li // you may not use this file except in compliance with the License.
5*ec63e07aSXin Li // You may obtain a copy of the License at
6*ec63e07aSXin Li //
7*ec63e07aSXin Li // https://www.apache.org/licenses/LICENSE-2.0
8*ec63e07aSXin Li //
9*ec63e07aSXin Li // Unless required by applicable law or agreed to in writing, software
10*ec63e07aSXin Li // distributed under the License is distributed on an "AS IS" BASIS,
11*ec63e07aSXin Li // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*ec63e07aSXin Li // See the License for the specific language governing permissions and
13*ec63e07aSXin Li // limitations under the License.
14*ec63e07aSXin Li
15*ec63e07aSXin Li #include "sandboxed_api/sandbox2/namespace.h"
16*ec63e07aSXin Li
17*ec63e07aSXin Li #include <unistd.h>
18*ec63e07aSXin Li
19*ec63e07aSXin Li #include <cstdint>
20*ec63e07aSXin Li #include <cstdlib>
21*ec63e07aSXin Li #include <initializer_list>
22*ec63e07aSXin Li #include <memory>
23*ec63e07aSXin Li #include <string>
24*ec63e07aSXin Li #include <utility>
25*ec63e07aSXin Li #include <vector>
26*ec63e07aSXin Li
27*ec63e07aSXin Li #include "gmock/gmock.h"
28*ec63e07aSXin Li #include "gtest/gtest.h"
29*ec63e07aSXin Li #include "absl/log/check.h"
30*ec63e07aSXin Li #include "absl/status/statusor.h"
31*ec63e07aSXin Li #include "absl/strings/str_cat.h"
32*ec63e07aSXin Li #include "absl/strings/string_view.h"
33*ec63e07aSXin Li #include "sandboxed_api/sandbox2/allow_all_syscalls.h"
34*ec63e07aSXin Li #include "sandboxed_api/sandbox2/executor.h"
35*ec63e07aSXin Li #include "sandboxed_api/sandbox2/policy.h"
36*ec63e07aSXin Li #include "sandboxed_api/sandbox2/policybuilder.h"
37*ec63e07aSXin Li #include "sandboxed_api/sandbox2/result.h"
38*ec63e07aSXin Li #include "sandboxed_api/sandbox2/sandbox2.h"
39*ec63e07aSXin Li #include "sandboxed_api/testing.h"
40*ec63e07aSXin Li #include "sandboxed_api/util/fileops.h"
41*ec63e07aSXin Li #include "sandboxed_api/util/status_matchers.h"
42*ec63e07aSXin Li #include "sandboxed_api/util/temp_file.h"
43*ec63e07aSXin Li
44*ec63e07aSXin Li namespace sandbox2 {
45*ec63e07aSXin Li namespace {
46*ec63e07aSXin Li
47*ec63e07aSXin Li namespace file_util = ::sapi::file_util;
48*ec63e07aSXin Li using ::sapi::CreateDefaultPermissiveTestPolicy;
49*ec63e07aSXin Li using ::sapi::CreateNamedTempFile;
50*ec63e07aSXin Li using ::sapi::GetTestSourcePath;
51*ec63e07aSXin Li using ::sapi::GetTestTempPath;
52*ec63e07aSXin Li using ::testing::AllOf;
53*ec63e07aSXin Li using ::testing::AnyOfArray;
54*ec63e07aSXin Li using ::testing::Contains;
55*ec63e07aSXin Li using ::testing::ElementsAre;
56*ec63e07aSXin Li using ::testing::Eq;
57*ec63e07aSXin Li using ::testing::Gt;
58*ec63e07aSXin Li using ::testing::HasSubstr;
59*ec63e07aSXin Li using ::testing::IsEmpty;
60*ec63e07aSXin Li using ::testing::Matcher;
61*ec63e07aSXin Li using ::testing::Ne;
62*ec63e07aSXin Li using ::testing::SizeIs;
63*ec63e07aSXin Li using ::testing::StartsWith;
64*ec63e07aSXin Li using ::testing::StrEq; // sapi::google3-only(broken matchers)
65*ec63e07aSXin Li
GetTestcaseBinPath(absl::string_view bin_name)66*ec63e07aSXin Li std::string GetTestcaseBinPath(absl::string_view bin_name) {
67*ec63e07aSXin Li return GetTestSourcePath(absl::StrCat("sandbox2/testcases/", bin_name));
68*ec63e07aSXin Li }
69*ec63e07aSXin Li
RunSandboxeeWithArgsAndPolicy(const std::string & bin_path,std::initializer_list<std::string> args,std::unique_ptr<Policy> policy=nullptr)70*ec63e07aSXin Li std::vector<std::string> RunSandboxeeWithArgsAndPolicy(
71*ec63e07aSXin Li const std::string& bin_path, std::initializer_list<std::string> args,
72*ec63e07aSXin Li std::unique_ptr<Policy> policy = nullptr) {
73*ec63e07aSXin Li if (!policy) {
74*ec63e07aSXin Li policy = CreateDefaultPermissiveTestPolicy(bin_path).BuildOrDie();
75*ec63e07aSXin Li }
76*ec63e07aSXin Li Sandbox2 sandbox(std::make_unique<Executor>(bin_path, args),
77*ec63e07aSXin Li std::move(policy));
78*ec63e07aSXin Li
79*ec63e07aSXin Li CHECK(sandbox.RunAsync());
80*ec63e07aSXin Li Comms* comms = sandbox.comms();
81*ec63e07aSXin Li uint64_t num;
82*ec63e07aSXin Li
83*ec63e07aSXin Li std::vector<std::string> entries;
84*ec63e07aSXin Li if (comms->RecvUint64(&num)) {
85*ec63e07aSXin Li entries.reserve(num);
86*ec63e07aSXin Li for (int i = 0; i < num; ++i) {
87*ec63e07aSXin Li std::string entry;
88*ec63e07aSXin Li CHECK(comms->RecvString(&entry));
89*ec63e07aSXin Li entries.push_back(std::move(entry));
90*ec63e07aSXin Li }
91*ec63e07aSXin Li }
92*ec63e07aSXin Li Result result = sandbox.AwaitResult();
93*ec63e07aSXin Li EXPECT_THAT(result.final_status(), Eq(Result::OK));
94*ec63e07aSXin Li EXPECT_THAT(result.reason_code(), Eq(0));
95*ec63e07aSXin Li return entries;
96*ec63e07aSXin Li }
97*ec63e07aSXin Li
TEST(NamespaceTest,FileNamespaceWorks)98*ec63e07aSXin Li TEST(NamespaceTest, FileNamespaceWorks) {
99*ec63e07aSXin Li // Mount /binary_path RO and check that it exists and is readable.
100*ec63e07aSXin Li // /etc/passwd should not exist.
101*ec63e07aSXin Li
102*ec63e07aSXin Li const std::string path = GetTestcaseBinPath("namespace");
103*ec63e07aSXin Li SAPI_ASSERT_OK_AND_ASSIGN(auto policy, CreateDefaultPermissiveTestPolicy(path)
104*ec63e07aSXin Li .AddFileAt(path, "/binary_path")
105*ec63e07aSXin Li .TryBuild());
106*ec63e07aSXin Li std::vector<std::string> result = RunSandboxeeWithArgsAndPolicy(
107*ec63e07aSXin Li path, {path, "0", "/binary_path", "/etc/passwd"}, std::move(policy));
108*ec63e07aSXin Li EXPECT_THAT(result, ElementsAre("/binary_path"));
109*ec63e07aSXin Li }
110*ec63e07aSXin Li
TEST(NamespaceTest,ReadOnlyIsRespected)111*ec63e07aSXin Li TEST(NamespaceTest, ReadOnlyIsRespected) {
112*ec63e07aSXin Li // Mount temporary file as RO and check that it actually is RO.
113*ec63e07aSXin Li auto [name, fd] = CreateNamedTempFile(GetTestTempPath("temp_file")).value();
114*ec63e07aSXin Li file_util::fileops::FDCloser temp_closer(fd);
115*ec63e07aSXin Li
116*ec63e07aSXin Li const std::string path = GetTestcaseBinPath("namespace");
117*ec63e07aSXin Li {
118*ec63e07aSXin Li SAPI_ASSERT_OK_AND_ASSIGN(auto policy,
119*ec63e07aSXin Li CreateDefaultPermissiveTestPolicy(path)
120*ec63e07aSXin Li .AddFileAt(name, "/temp_file")
121*ec63e07aSXin Li .TryBuild());
122*ec63e07aSXin Li // Check that it is readable
123*ec63e07aSXin Li std::vector<std::string> result = RunSandboxeeWithArgsAndPolicy(
124*ec63e07aSXin Li path, {path, "0", "/temp_file"}, std::move(policy));
125*ec63e07aSXin Li EXPECT_THAT(result, ElementsAre("/temp_file"));
126*ec63e07aSXin Li }
127*ec63e07aSXin Li {
128*ec63e07aSXin Li SAPI_ASSERT_OK_AND_ASSIGN(auto policy,
129*ec63e07aSXin Li CreateDefaultPermissiveTestPolicy(path)
130*ec63e07aSXin Li .AddFileAt(name, "/temp_file")
131*ec63e07aSXin Li .TryBuild());
132*ec63e07aSXin Li // Now check that it is not writeable
133*ec63e07aSXin Li std::vector<std::string> result = RunSandboxeeWithArgsAndPolicy(
134*ec63e07aSXin Li path, {path, "1", "/temp_file"}, std::move(policy));
135*ec63e07aSXin Li EXPECT_THAT(result, IsEmpty());
136*ec63e07aSXin Li }
137*ec63e07aSXin Li }
138*ec63e07aSXin Li
TEST(NamespaceTest,UserNamespaceWorks)139*ec63e07aSXin Li TEST(NamespaceTest, UserNamespaceWorks) {
140*ec63e07aSXin Li const std::string path = GetTestcaseBinPath("namespace");
141*ec63e07aSXin Li
142*ec63e07aSXin Li // Check that getpid() returns 2 (which is the case inside pid NS).
143*ec63e07aSXin Li {
144*ec63e07aSXin Li std::vector<std::string> result =
145*ec63e07aSXin Li RunSandboxeeWithArgsAndPolicy(path, {path, "2"});
146*ec63e07aSXin Li EXPECT_THAT(result, ElementsAre("2"));
147*ec63e07aSXin Li }
148*ec63e07aSXin Li
149*ec63e07aSXin Li // Validate that getpid() does not return 2 when outside of a pid NS.
150*ec63e07aSXin Li {
151*ec63e07aSXin Li std::vector<std::string> result = RunSandboxeeWithArgsAndPolicy(
152*ec63e07aSXin Li path, {path, "2"},
153*ec63e07aSXin Li PolicyBuilder()
154*ec63e07aSXin Li .DisableNamespaces()
155*ec63e07aSXin Li .DefaultAction(AllowAllSyscalls()) // Do not restrict syscalls
156*ec63e07aSXin Li .BuildOrDie());
157*ec63e07aSXin Li EXPECT_THAT(result, ElementsAre(Ne("2")));
158*ec63e07aSXin Li }
159*ec63e07aSXin Li }
160*ec63e07aSXin Li
TEST(NamespaceTest,UserNamespaceIDMapWritten)161*ec63e07aSXin Li TEST(NamespaceTest, UserNamespaceIDMapWritten) {
162*ec63e07aSXin Li // Check that the idmap is initialized before the sandbox application is
163*ec63e07aSXin Li // started.
164*ec63e07aSXin Li const std::string path = GetTestcaseBinPath("namespace");
165*ec63e07aSXin Li {
166*ec63e07aSXin Li std::vector<std::string> result =
167*ec63e07aSXin Li RunSandboxeeWithArgsAndPolicy(path, {path, "3", "1000", "1000"});
168*ec63e07aSXin Li EXPECT_THAT(result, ElementsAre("1000", "1000"));
169*ec63e07aSXin Li }
170*ec63e07aSXin Li
171*ec63e07aSXin Li // Check that the uid/gid is the same when not using namespaces.
172*ec63e07aSXin Li {
173*ec63e07aSXin Li std::vector<std::string> result = RunSandboxeeWithArgsAndPolicy(
174*ec63e07aSXin Li path, {path, "3"},
175*ec63e07aSXin Li PolicyBuilder()
176*ec63e07aSXin Li .DisableNamespaces()
177*ec63e07aSXin Li .DefaultAction(AllowAllSyscalls()) // Do not restrict syscalls
178*ec63e07aSXin Li .BuildOrDie());
179*ec63e07aSXin Li EXPECT_THAT(result,
180*ec63e07aSXin Li ElementsAre(absl::StrCat(getuid()), absl::StrCat(getgid())));
181*ec63e07aSXin Li }
182*ec63e07aSXin Li }
183*ec63e07aSXin Li
TEST(NamespaceTest,RootReadOnly)184*ec63e07aSXin Li TEST(NamespaceTest, RootReadOnly) {
185*ec63e07aSXin Li // Mount rw tmpfs at /tmp and check it is RW.
186*ec63e07aSXin Li // Check also that / is RO.
187*ec63e07aSXin Li const std::string path = GetTestcaseBinPath("namespace");
188*ec63e07aSXin Li SAPI_ASSERT_OK_AND_ASSIGN(
189*ec63e07aSXin Li auto policy, CreateDefaultPermissiveTestPolicy(path)
190*ec63e07aSXin Li .AddTmpfs("/tmp", /*size=*/4ULL << 20 /* 4 MiB */)
191*ec63e07aSXin Li .TryBuild());
192*ec63e07aSXin Li std::vector<std::string> result = RunSandboxeeWithArgsAndPolicy(
193*ec63e07aSXin Li path, {path, "4", "/tmp/testfile", "/testfile"}, std::move(policy));
194*ec63e07aSXin Li EXPECT_THAT(result, ElementsAre("/tmp/testfile"));
195*ec63e07aSXin Li }
196*ec63e07aSXin Li
TEST(NamespaceTest,RootWritable)197*ec63e07aSXin Li TEST(NamespaceTest, RootWritable) {
198*ec63e07aSXin Li // Mount root rw and check it
199*ec63e07aSXin Li const std::string path = GetTestcaseBinPath("namespace");
200*ec63e07aSXin Li SAPI_ASSERT_OK_AND_ASSIGN(
201*ec63e07aSXin Li auto policy,
202*ec63e07aSXin Li CreateDefaultPermissiveTestPolicy(path).SetRootWritable().TryBuild());
203*ec63e07aSXin Li std::vector<std::string> result = RunSandboxeeWithArgsAndPolicy(
204*ec63e07aSXin Li path, {path, "4", "/testfile"}, std::move(policy));
205*ec63e07aSXin Li EXPECT_THAT(result, ElementsAre("/testfile"));
206*ec63e07aSXin Li }
207*ec63e07aSXin Li
TEST(NamespaceTest,HostnameNone)208*ec63e07aSXin Li TEST(NamespaceTest, HostnameNone) {
209*ec63e07aSXin Li const std::string path = GetTestcaseBinPath("namespace");
210*ec63e07aSXin Li std::vector<std::string> result = RunSandboxeeWithArgsAndPolicy(
211*ec63e07aSXin Li path, {path, "7"},
212*ec63e07aSXin Li PolicyBuilder()
213*ec63e07aSXin Li .DisableNamespaces()
214*ec63e07aSXin Li .DefaultAction(AllowAllSyscalls()) // Do not restrict syscalls
215*ec63e07aSXin Li .BuildOrDie());
216*ec63e07aSXin Li EXPECT_THAT(result, ElementsAre(Ne("sandbox2")));
217*ec63e07aSXin Li }
218*ec63e07aSXin Li
TEST(NamespaceTest,HostnameDefault)219*ec63e07aSXin Li TEST(NamespaceTest, HostnameDefault) {
220*ec63e07aSXin Li const std::string path = GetTestcaseBinPath("namespace");
221*ec63e07aSXin Li std::vector<std::string> result =
222*ec63e07aSXin Li RunSandboxeeWithArgsAndPolicy(path, {path, "7"});
223*ec63e07aSXin Li EXPECT_THAT(result, ElementsAre("sandbox2"));
224*ec63e07aSXin Li }
225*ec63e07aSXin Li
TEST(NamespaceTest,HostnameConfigured)226*ec63e07aSXin Li TEST(NamespaceTest, HostnameConfigured) {
227*ec63e07aSXin Li const std::string path = GetTestcaseBinPath("namespace");
228*ec63e07aSXin Li SAPI_ASSERT_OK_AND_ASSIGN(auto policy, CreateDefaultPermissiveTestPolicy(path)
229*ec63e07aSXin Li .SetHostname("configured")
230*ec63e07aSXin Li .TryBuild());
231*ec63e07aSXin Li std::vector<std::string> result =
232*ec63e07aSXin Li RunSandboxeeWithArgsAndPolicy(path, {path, "7"}, std::move(policy));
233*ec63e07aSXin Li EXPECT_THAT(result, ElementsAre("configured"));
234*ec63e07aSXin Li }
235*ec63e07aSXin Li
TEST(NamespaceTest,TestInterfacesNoNetwork)236*ec63e07aSXin Li TEST(NamespaceTest, TestInterfacesNoNetwork) {
237*ec63e07aSXin Li const std::string path = GetTestcaseBinPath("namespace");
238*ec63e07aSXin Li std::vector<std::string> result =
239*ec63e07aSXin Li RunSandboxeeWithArgsAndPolicy(path, {path, "5"});
240*ec63e07aSXin Li // Only loopback network interface 'lo'.
241*ec63e07aSXin Li EXPECT_THAT(result, ElementsAre("lo"));
242*ec63e07aSXin Li }
243*ec63e07aSXin Li
TEST(NamespaceTest,TestInterfacesWithNetwork)244*ec63e07aSXin Li TEST(NamespaceTest, TestInterfacesWithNetwork) {
245*ec63e07aSXin Li const std::string path = GetTestcaseBinPath("namespace");
246*ec63e07aSXin Li SAPI_ASSERT_OK_AND_ASSIGN(auto policy, CreateDefaultPermissiveTestPolicy(path)
247*ec63e07aSXin Li .AllowUnrestrictedNetworking()
248*ec63e07aSXin Li .TryBuild());
249*ec63e07aSXin Li
250*ec63e07aSXin Li std::vector<std::string> result =
251*ec63e07aSXin Li RunSandboxeeWithArgsAndPolicy(path, {path, "5"}, std::move(policy));
252*ec63e07aSXin Li // Loopback network interface 'lo' and more.
253*ec63e07aSXin Li EXPECT_THAT(result, Contains("lo"));
254*ec63e07aSXin Li EXPECT_THAT(result, SizeIs(Gt(1)));
255*ec63e07aSXin Li }
256*ec63e07aSXin Li
TEST(NamespaceTest,TestFiles)257*ec63e07aSXin Li TEST(NamespaceTest, TestFiles) {
258*ec63e07aSXin Li SKIP_ANDROID;
259*ec63e07aSXin Li const std::string path = GetTestcaseBinPath("namespace");
260*ec63e07aSXin Li std::vector<std::string> result =
261*ec63e07aSXin Li RunSandboxeeWithArgsAndPolicy(path, {path, "6", "/"});
262*ec63e07aSXin Li
263*ec63e07aSXin Li std::vector<Matcher<std::string>> lib_paths = {
264*ec63e07aSXin Li StartsWith("/lib/"), // Often a symlink -> /usr/lib
265*ec63e07aSXin Li StartsWith("/usr/lib/"),
266*ec63e07aSXin Li StartsWith("/lib64/"), // Often a symlink -> /usr/lib64
267*ec63e07aSXin Li StartsWith("/usr/lib64/")};
268*ec63e07aSXin Li auto correct_lib_path_matcher =
269*ec63e07aSXin Li AllOf(HasSubstr(".so"), AnyOfArray(lib_paths));
270*ec63e07aSXin Li std::vector<Matcher<std::string>> matchers = {
271*ec63e07aSXin Li correct_lib_path_matcher,
272*ec63e07aSXin Li // Conditionally mapped if Tomoyo is active
273*ec63e07aSXin Li StrEq(absl::StrCat("/dev/fd/", Comms::kSandbox2TargetExecFD)),
274*ec63e07aSXin Li // System ldconfig cache
275*ec63e07aSXin Li StrEq("/etc/ld.so.cache"),
276*ec63e07aSXin Li // GRTE ldconfig cache
277*ec63e07aSXin Li StrEq("/usr/grte/v4/etc/ld.so.cache"),
278*ec63e07aSXin Li StrEq("/usr/grte/v5/etc/ld.so.cache"),
279*ec63e07aSXin Li // procfs and sysfs
280*ec63e07aSXin Li StartsWith("/proc"), StartsWith("/sys")};
281*ec63e07aSXin Li // Coverage DIR
282*ec63e07aSXin Li char* coverage_dir = getenv("COVERAGE_DIR");
283*ec63e07aSXin Li if (coverage_dir != nullptr) {
284*ec63e07aSXin Li matchers.push_back(StartsWith(coverage_dir));
285*ec63e07aSXin Li }
286*ec63e07aSXin Li for (const auto& file : result) {
287*ec63e07aSXin Li EXPECT_THAT(file, AnyOfArray(matchers));
288*ec63e07aSXin Li }
289*ec63e07aSXin Li }
290*ec63e07aSXin Li
291*ec63e07aSXin Li } // namespace
292*ec63e07aSXin Li } // namespace sandbox2
293