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/policybuilder.h"
16
17 #include <syscall.h>
18 #include <unistd.h>
19
20 #include <cerrno>
21 #include <memory>
22 #include <string>
23 #include <vector>
24
25 #include "gmock/gmock.h"
26 #include "gtest/gtest.h"
27 #include "absl/status/status.h"
28 #include "absl/status/statusor.h"
29 #include "absl/strings/string_view.h"
30 #include "sandboxed_api/sandbox2/policy.h"
31 #include "sandboxed_api/sandbox2/util/bpf_helper.h"
32 #include "sandboxed_api/sandbox2/violation.pb.h"
33 #include "sandboxed_api/util/status_matchers.h"
34
35 namespace sandbox2 {
36
37 class PolicyBuilderPeer {
38 public:
PolicyBuilderPeer(PolicyBuilder * builder)39 explicit PolicyBuilderPeer(PolicyBuilder* builder) : builder_{builder} {}
40
policy_size() const41 int policy_size() const { return builder_->user_policy_.size(); }
42
ValidateAbsolutePath(absl::string_view path)43 static absl::StatusOr<std::string> ValidateAbsolutePath(
44 absl::string_view path) {
45 return PolicyBuilder::ValidateAbsolutePath(path);
46 }
47
48 private:
49 PolicyBuilder* builder_;
50 };
51
52 namespace {
53
54 using ::sapi::IsOk;
55 using ::sapi::StatusIs;
56 using ::testing::Eq;
57 using ::testing::Lt;
58 using ::testing::StartsWith;
59 using ::testing::StrEq;
60
TEST(PolicyBuilderTest,Testpolicy_size)61 TEST(PolicyBuilderTest, Testpolicy_size) {
62 ssize_t last_size = 0;
63 PolicyBuilder builder;
64 PolicyBuilderPeer builder_peer{&builder};
65
66 auto assert_increased = [&last_size, &builder_peer]() {
67 ASSERT_THAT(last_size, Lt(builder_peer.policy_size()));
68 last_size = builder_peer.policy_size();
69 };
70
71 auto assert_same = [&last_size, &builder_peer]() {
72 ASSERT_THAT(last_size, Eq(builder_peer.policy_size()));
73 };
74
75 // clang-format off
76 assert_same();
77
78 builder.AllowSyscall(__NR_chroot); assert_increased();
79 builder.AllowSyscall(__NR_chroot); assert_same();
80 builder.AllowSyscall(__NR_umask); assert_increased();
81 builder.AllowSyscall(__NR_umask); assert_same();
82 builder.AllowSyscall(__NR_chroot); assert_same();
83 builder.AllowSyscall(__NR_chroot); assert_same();
84
85 builder.AllowSystemMalloc(); assert_increased();
86 builder.AllowSyscall(__NR_munmap); assert_same();
87 builder.BlockSyscallWithErrno(__NR_munmap, 1); assert_same();
88 builder.BlockSyscallWithErrno(__NR_openat, 1);
89 assert_increased();
90
91 builder.AllowTCGETS(); assert_increased();
92 builder.AllowTCGETS(); assert_increased();
93 builder.AllowTCGETS(); assert_increased();
94
95 builder.AddPolicyOnSyscall(__NR_fchmod, { ALLOW }); assert_increased();
96 builder.AddPolicyOnSyscall(__NR_fchmod, { ALLOW }); assert_increased();
97
98 builder.AddPolicyOnSyscalls({ __NR_fchmod, __NR_chdir }, { ALLOW });
99 assert_increased();
100 builder.AddPolicyOnSyscalls({ __NR_fchmod, __NR_chdir }, { ALLOW });
101 assert_increased();
102
103 // This might change in the future if we implement an optimization.
104 builder.AddPolicyOnSyscall(__NR_umask, { ALLOW }); assert_increased();
105 builder.AddPolicyOnSyscall(__NR_umask, { ALLOW }); assert_increased();
106
107 // None of the namespace functions should alter the seccomp policy.
108 builder.AddFile("/usr/bin/find"); assert_same();
109 builder.AddDirectory("/bin"); assert_same();
110 builder.AddTmpfs("/tmp", /*size=*/4ULL << 20 /* 4 MiB */); assert_same();
111 builder.AllowUnrestrictedNetworking(); assert_same();
112 // clang-format on
113 }
114
TEST(PolicyBuilderTest,TestValidateAbsolutePath)115 TEST(PolicyBuilderTest, TestValidateAbsolutePath) {
116 for (auto const& bad_path : {
117 "..",
118 "a",
119 "a/b",
120 "a/b/c",
121 "/a/b/c/../d",
122 "/a/b/c/./d",
123 "/a/b/c//d",
124 "/a/b/c/d/",
125 "/a/bAAAAAAAAAAAAAAAAAAAAAA/c/d/",
126 }) {
127 EXPECT_THAT(PolicyBuilderPeer::ValidateAbsolutePath(bad_path),
128 StatusIs(absl::StatusCode::kInvalidArgument));
129 }
130
131 for (auto const& good_path :
132 {"/", "/a/b/c/d", "/a/b/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"}) {
133 SAPI_ASSERT_OK_AND_ASSIGN(
134 std::string path, PolicyBuilderPeer::ValidateAbsolutePath(good_path));
135 EXPECT_THAT(path, StrEq(good_path));
136 }
137 }
138
TEST(PolicyBuilderTest,TestCanOnlyBuildOnce)139 TEST(PolicyBuilderTest, TestCanOnlyBuildOnce) {
140 PolicyBuilder b;
141 ASSERT_THAT(b.TryBuild(), IsOk());
142 EXPECT_THAT(b.TryBuild(), StatusIs(absl::StatusCode::kFailedPrecondition,
143 "Can only build policy once."));
144 }
145
TEST(PolicyBuilderTest,TestIsCopyable)146 TEST(PolicyBuilderTest, TestIsCopyable) {
147 PolicyBuilder builder;
148 builder.AllowSyscall(__NR_getpid);
149
150 PolicyBuilder copy = builder;
151 ASSERT_EQ(PolicyBuilderPeer(©).policy_size(),
152 PolicyBuilderPeer(&builder).policy_size());
153
154 // Both can be built.
155 EXPECT_THAT(builder.TryBuild(), IsOk());
156 EXPECT_THAT(copy.TryBuild(), IsOk());
157 }
158
TEST(PolicyBuilderTest,CanBypassPtrace)159 TEST(PolicyBuilderTest, CanBypassPtrace) {
160 PolicyBuilder builder;
161 builder.AddPolicyOnSyscall(__NR_ptrace, {ALLOW})
162 .BlockSyscallWithErrno(__NR_ptrace, ENOENT);
163 EXPECT_THAT(builder.TryBuild(), Not(IsOk()));
164 }
165
TEST(PolicyBuilderTest,AddPolicyOnSyscallsNoEmptyList)166 TEST(PolicyBuilderTest, AddPolicyOnSyscallsNoEmptyList) {
167 PolicyBuilder builder;
168 builder.AddPolicyOnSyscalls({}, {ALLOW});
169 EXPECT_THAT(builder.TryBuild(), StatusIs(absl::StatusCode::kInvalidArgument));
170 }
171
TEST(PolicyBuilderTest,AddPolicyOnSyscallJumpOutOfBounds)172 TEST(PolicyBuilderTest, AddPolicyOnSyscallJumpOutOfBounds) {
173 PolicyBuilder builder;
174 builder.AddPolicyOnSyscall(__NR_write,
175 {BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 1, 2, 0)});
176 EXPECT_THAT(builder.TryBuild(), StatusIs(absl::StatusCode::kInvalidArgument));
177 }
178 } // namespace
179 } // namespace sandbox2
180