• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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