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