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