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 // A demo sandbox for the static_bin binary.
16 // Use: static_sandbox --logtostderr
17
18 #include <fcntl.h>
19 #include <sys/mman.h>
20 #include <syscall.h>
21 #include <unistd.h>
22
23 #include <cerrno>
24 #include <cstdlib>
25 #include <memory>
26 #include <string>
27 #include <utility>
28 #include <vector>
29
30 #include "absl/log/check.h"
31 #include "absl/flags/parse.h"
32 #include "absl/log/globals.h"
33 #include "absl/log/initialize.h"
34 #include "absl/log/log.h"
35 #include "absl/base/log_severity.h"
36 #include "absl/strings/string_view.h"
37 #include "absl/time/time.h"
38 #include "sandboxed_api/config.h"
39 #include "sandboxed_api/sandbox2/executor.h"
40 #include "sandboxed_api/sandbox2/limits.h"
41 #include "sandboxed_api/sandbox2/policy.h"
42 #include "sandboxed_api/sandbox2/policybuilder.h"
43 #include "sandboxed_api/sandbox2/result.h"
44 #include "sandboxed_api/sandbox2/sandbox2.h"
45 #include "sandboxed_api/sandbox2/util/bpf_helper.h"
46 #include "sandboxed_api/util/runfiles.h"
47
GetPolicy()48 std::unique_ptr<sandbox2::Policy> GetPolicy() {
49 return sandbox2::PolicyBuilder()
50 // The most frequent syscall should go first in this sequence (to make it
51 // fast).
52 // Allow read() with all arguments.
53 .AllowRead()
54 // Allow a preset of syscalls that are known to be used during startup
55 // of static binaries.
56 .AllowStaticStartup()
57 // Allow the getpid() syscall.
58 .AllowSyscall(__NR_getpid)
59
60 // Examples for AddPolicyOnSyscall:
61 .AddPolicyOnSyscall(__NR_write,
62 {
63 // Load the first argument of write() (= fd)
64 ARG_32(0),
65 // Allow write(fd=STDOUT)
66 JEQ32(1, ALLOW),
67 // Allow write(fd=STDERR)
68 JEQ32(2, ALLOW),
69 // Fall-through for every other case.
70 // The default action will be KILL if it is not
71 // explicitly ALLOWed by a following rule.
72 })
73 // write() calls with fd not in (1, 2) will continue evaluating the
74 // policy. This means that other rules might still allow them.
75
76 // Allow the Sandboxee to set the name for better recognition in the
77 // process listing.
78 .AllowPrctlSetName()
79
80 // Allow the dynamic loader to mark pages to never allow read-write-exec.
81 .AddPolicyOnSyscall(__NR_mprotect,
82 {
83 ARG_32(2),
84 JEQ32(PROT_READ, ALLOW),
85 JEQ32(PROT_NONE, ALLOW),
86 JEQ32(PROT_READ | PROT_WRITE, ALLOW),
87 JEQ32(PROT_READ | PROT_EXEC, ALLOW),
88 })
89
90 // Allow exit() only with an exit_code of 0.
91 // Explicitly jumping to KILL, thus the following rules can not
92 // override this rule.
93 .AddPolicyOnSyscall(
94 __NR_exit_group,
95 {
96 // Load first argument (exit_code).
97 ARG_32(0),
98 // Deny every argument except 0.
99 JNE32(0, KILL),
100 // Allow all exit() calls that were not previously forbidden
101 // = exit_code == 0.
102 ALLOW,
103 })
104
105 // = This won't have any effect as we handled every case of this syscall
106 // in the previous rule.
107 .AllowSyscall(__NR_exit_group)
108
109 .BlockSyscallsWithErrno(
110 {
111 #ifdef __NR_access
112 // On Debian, even static binaries check existence of
113 // /etc/ld.so.nohwcap.
114 __NR_access,
115 #endif
116 __NR_faccessat,
117
118 #ifdef __NR_open
119 __NR_open,
120 #endif
121 __NR_openat,
122 },
123 ENOENT)
124 .BuildOrDie();
125 }
126
main(int argc,char * argv[])127 int main(int argc, char* argv[]) {
128 // This test is incompatible with sanitizers.
129 // The `SKIP_SANITIZERS_AND_COVERAGE` macro won't work for us here since we
130 // need to return something.
131 if constexpr (sapi::sanitizers::IsAny()) {
132 return EXIT_SUCCESS;
133 }
134 absl::SetStderrThreshold(absl::LogSeverityAtLeast::kInfo);
135 absl::ParseCommandLine(argc, argv);
136 absl::InitializeLog();
137
138 // Note: In your own code, use sapi::GetDataDependencyFilePath() instead.
139 const std::string path = sapi::internal::GetSapiDataDependencyFilePath(
140 "sandbox2/examples/static/static_bin");
141 std::vector<std::string> args = {path};
142 auto executor = std::make_unique<sandbox2::Executor>(path, args);
143
144 executor
145 // Sandboxing is enabled by the sandbox itself. The sandboxed binary is
146 // not aware that it'll be sandboxed.
147 // Note: 'true' is the default setting for this class.
148 ->set_enable_sandbox_before_exec(true)
149 .limits()
150 // Kill sandboxed processes with a signal (SIGXFSZ) if it writes more than
151 // these many bytes to the file-system.
152 ->set_rlimit_fsize(1024 * 1024)
153 // The CPU time limit.
154 .set_rlimit_cpu(60)
155 .set_walltime_limit(absl::Seconds(30));
156
157 int proc_version_fd = open("/proc/version", O_RDONLY);
158 PCHECK(proc_version_fd != -1);
159
160 // Map this fils to sandboxee's stdin.
161 executor->ipc()->MapFd(proc_version_fd, STDIN_FILENO);
162
163 auto policy = GetPolicy();
164 sandbox2::Sandbox2 s2(std::move(executor), std::move(policy));
165
166 // Let the sandboxee run (synchronously).
167 sandbox2::Result result = s2.Run();
168
169 LOG(INFO) << "Final execution status: " << result.ToString();
170
171 return result.final_status() == sandbox2::Result::OK ? EXIT_SUCCESS
172 : EXIT_FAILURE;
173 }
174