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/notify.h"
16
17 #include <sys/types.h>
18 #include <syscall.h>
19
20 #include <memory>
21 #include <string>
22 #include <utility>
23 #include <vector>
24
25 #include "gmock/gmock.h"
26 #include "gtest/gtest.h"
27 #include "absl/log/log.h"
28 #include "absl/status/status.h"
29 #include "absl/strings/str_join.h"
30 #include "absl/strings/string_view.h"
31 #include "sandboxed_api/sandbox2/allowlists/trace_all_syscalls.h"
32 #include "sandboxed_api/sandbox2/comms.h"
33 #include "sandboxed_api/sandbox2/executor.h"
34 #include "sandboxed_api/sandbox2/policy.h"
35 #include "sandboxed_api/sandbox2/policybuilder.h"
36 #include "sandboxed_api/sandbox2/result.h"
37 #include "sandboxed_api/sandbox2/sandbox2.h"
38 #include "sandboxed_api/sandbox2/syscall.h"
39 #include "sandboxed_api/testing.h"
40 #include "sandboxed_api/util/status_matchers.h"
41
42 namespace sandbox2 {
43 namespace {
44
45 using ::sapi::CreateDefaultPermissiveTestPolicy;
46 using ::sapi::GetTestSourcePath;
47 using ::sapi::IsOk;
48 using ::testing::Eq;
49
50 // If syscall and its arguments don't match the expected ones, return the
51 // opposite of the requested values (allow/disallow) to indicate an error.
52 class PersonalityNotify : public Notify {
53 public:
PersonalityNotify(bool allow)54 explicit PersonalityNotify(bool allow) : allow_(allow) {}
55
EventSyscallTrap(const Syscall & syscall)56 bool EventSyscallTrap(const Syscall& syscall) override {
57 if (syscall.nr() != __NR_personality) {
58 LOG(ERROR) << "kSyscall==" << syscall.nr();
59 return (!allow_);
60 }
61 Syscall::Args expected_args = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6};
62 if (syscall.args() != expected_args) {
63 LOG(ERROR) << "args=={" << absl::StrJoin(syscall.args(), ", ") << "}";
64 return (!allow_);
65 }
66 return allow_;
67 }
68
69 private:
70 // The intended return value from EventSyscallTrap in case all registers
71 // match.
72 bool allow_;
73 };
74
75 // Print the newly created PID, and exchange data over Comms before sandboxing.
76 class PidCommsNotify : public Notify {
77 public:
EventStarted(pid_t pid,Comms * comms)78 bool EventStarted(pid_t pid, Comms* comms) final {
79 LOG(INFO) << "The newly created PID: " << pid;
80 bool v;
81 return comms->RecvBool(&v);
82 }
83 };
84
85 class FinishedNotify : public Notify {
86 public:
IsFinished()87 bool IsFinished() { return finished_; }
EventStarted(pid_t pid,Comms * comms)88 bool EventStarted(pid_t pid, Comms* comms) override {
89 EXPECT_FALSE(finished_);
90 return true;
91 }
EventFinished(const Result & result)92 void EventFinished(const Result& result) override { finished_ = true; }
93
94 private:
95 bool finished_ = false;
96 };
97
98 class NotifyTest : public ::testing::TestWithParam<bool> {
99 public:
100 // Allow typical syscalls and call SECCOMP_RET_TRACE for personality syscall,
101 // chosen because unlikely to be called by a regular program.
NotifyTestcasePolicy(absl::string_view path)102 std::unique_ptr<Policy> NotifyTestcasePolicy(absl::string_view path) {
103 sandbox2::PolicyBuilder builder =
104 CreateDefaultPermissiveTestPolicy(path).AddPolicyOnSyscall(
105 __NR_personality, {SANDBOX2_TRACE});
106 if (GetParam()) {
107 builder.CollectStacktracesOnSignal(false);
108 }
109 return builder.BuildOrDie();
110 }
SetUpSandbox(Sandbox2 * sandbox)111 absl::Status SetUpSandbox(Sandbox2* sandbox) {
112 return GetParam() ? sandbox->EnableUnotifyMonitor() : absl::OkStatus();
113 }
114 };
115
116 // Test EventSyscallTrap on personality syscall and allow it.
TEST_P(NotifyTest,AllowPersonality)117 TEST_P(NotifyTest, AllowPersonality) {
118 const std::string path = GetTestSourcePath("sandbox2/testcases/personality");
119 std::vector<std::string> args = {path};
120 Sandbox2 s2(std::make_unique<Executor>(path, args),
121 NotifyTestcasePolicy(path),
122 std::make_unique<PersonalityNotify>(/*allow=*/true));
123 ASSERT_THAT(SetUpSandbox(&s2), IsOk());
124 auto result = s2.Run();
125
126 ASSERT_THAT(result.final_status(), Eq(Result::OK));
127 EXPECT_THAT(result.reason_code(), Eq(22));
128 }
129
130 // Test EventSyscallTrap on personality syscall and disallow it.
TEST_P(NotifyTest,DisallowPersonality)131 TEST_P(NotifyTest, DisallowPersonality) {
132 const std::string path = GetTestSourcePath("sandbox2/testcases/personality");
133 std::vector<std::string> args = {path};
134 Sandbox2 s2(std::make_unique<Executor>(path, args),
135 NotifyTestcasePolicy(path),
136 std::make_unique<PersonalityNotify>(/*allow=*/false));
137 ASSERT_THAT(SetUpSandbox(&s2), IsOk());
138 auto result = s2.Run();
139
140 ASSERT_THAT(result.final_status(), Eq(Result::VIOLATION));
141 EXPECT_THAT(result.reason_code(), Eq(__NR_personality));
142 }
143
144 // Test EventStarted by exchanging data after started but before sandboxed.
TEST_P(NotifyTest,PrintPidAndComms)145 TEST_P(NotifyTest, PrintPidAndComms) {
146 const std::string path = GetTestSourcePath("sandbox2/testcases/pidcomms");
147 std::vector<std::string> args = {path};
148 auto executor = std::make_unique<Executor>(path, args);
149 executor->set_enable_sandbox_before_exec(false);
150
151 Sandbox2 s2(std::move(executor), NotifyTestcasePolicy(path),
152 std::make_unique<PidCommsNotify>());
153 ASSERT_THAT(SetUpSandbox(&s2), IsOk());
154 auto result = s2.Run();
155
156 ASSERT_THAT(result.final_status(), Eq(Result::OK));
157 EXPECT_THAT(result.reason_code(), Eq(33));
158 }
159
160 // Test EventFinished by exchanging data after started but before sandboxed.
TEST_P(NotifyTest,EventFinished)161 TEST_P(NotifyTest, EventFinished) {
162 const std::string path = GetTestSourcePath("sandbox2/testcases/minimal");
163 std::vector<std::string> args = {path};
164 auto executor = std::make_unique<Executor>(path, args);
165
166 auto notify = std::make_unique<FinishedNotify>();
167 FinishedNotify* notify_ptr = notify.get();
168 Sandbox2 s2(std::move(executor), NotifyTestcasePolicy(path),
169 std::move(notify));
170 ASSERT_THAT(SetUpSandbox(&s2), IsOk());
171 EXPECT_FALSE(notify_ptr->IsFinished());
172 auto result = s2.Run();
173 EXPECT_TRUE(notify_ptr->IsFinished());
174
175 ASSERT_THAT(result.final_status(), Eq(Result::OK));
176 EXPECT_THAT(result.reason_code(), Eq(0));
177 }
178
179 // Test EventSyscallTrap on personality syscall through TraceAllSyscalls
TEST_P(NotifyTest,TraceAllAllowPersonality)180 TEST_P(NotifyTest, TraceAllAllowPersonality) {
181 const std::string path = GetTestSourcePath("sandbox2/testcases/personality");
182 std::vector<std::string> args = {path};
183 auto policy = CreateDefaultPermissiveTestPolicy(path)
184 .DefaultAction(TraceAllSyscalls())
185 .BuildOrDie();
186 Sandbox2 s2(std::make_unique<Executor>(path, args),
187 NotifyTestcasePolicy(path),
188 std::make_unique<PersonalityNotify>(/*allow=*/true));
189
190 ASSERT_THAT(SetUpSandbox(&s2), IsOk());
191 auto result = s2.Run();
192
193 ASSERT_THAT(result.final_status(), Eq(Result::OK));
194 EXPECT_THAT(result.reason_code(), Eq(22));
195 }
196
197 INSTANTIATE_TEST_SUITE_P(Notify, NotifyTest, ::testing::Values(false, true),
__anon5e7d95510202(const ::testing::TestParamInfo<bool>& info) 198 [](const ::testing::TestParamInfo<bool>& info) {
199 return info.param ? "UnotifyMonitor"
200 : "PtraceMonitor";
201 });
202
203 } // namespace
204 } // namespace sandbox2
205