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 crc4bin binary
16
17 #include <syscall.h>
18
19 #include <cstdint>
20 #include <cstdio>
21 #include <cstdlib>
22 #include <memory>
23 #include <string>
24 #include <utility>
25 #include <vector>
26
27 #include "absl/flags/flag.h"
28 #include "absl/flags/parse.h"
29 #include "absl/log/globals.h"
30 #include "absl/log/initialize.h"
31 #include "absl/log/log.h"
32 #include "absl/base/log_severity.h"
33 #include "absl/strings/string_view.h"
34 #include "absl/time/time.h"
35 #include "sandboxed_api/sandbox2/allowlists/namespaces.h"
36 #include "sandboxed_api/sandbox2/comms.h"
37 #include "sandboxed_api/sandbox2/executor.h"
38 #include "sandboxed_api/sandbox2/limits.h"
39 #include "sandboxed_api/sandbox2/policy.h"
40 #include "sandboxed_api/sandbox2/policybuilder.h"
41 #include "sandboxed_api/sandbox2/result.h"
42 #include "sandboxed_api/sandbox2/sandbox2.h"
43 #include "sandboxed_api/sandbox2/util/bpf_helper.h"
44 #include "sandboxed_api/util/runfiles.h"
45
46 ABSL_FLAG(std::string, input, "", "Input to calculate CRC4 of.");
47 ABSL_FLAG(bool, call_syscall_not_allowed, false,
48 "Have sandboxee call clone (violation).");
49
50 namespace {
51
GetPolicy()52 std::unique_ptr<sandbox2::Policy> GetPolicy() {
53 return sandbox2::PolicyBuilder()
54 .DisableNamespaces(sandbox2::NamespacesToken()) // Safe, as we only allow
55 // I/O on existing FDs.
56 .AllowExit()
57 .AddPolicyOnSyscalls(
58 {
59 __NR_read,
60 __NR_write,
61 __NR_close,
62 },
63 {
64 ARG_32(0),
65 JEQ32(sandbox2::Comms::kSandbox2ClientCommsFD, ALLOW),
66 })
67 .AllowLlvmSanitizers() // Will be a no-op when not using sanitizers.
68 .BuildOrDie();
69 }
70
SandboxedCRC4(sandbox2::Comms * comms,uint32_t * crc4)71 bool SandboxedCRC4(sandbox2::Comms* comms, uint32_t* crc4) {
72 const std::string input = absl::GetFlag(FLAGS_input);
73
74 auto* buf = reinterpret_cast<const uint8_t*>(input.data());
75 size_t buf_size = input.size();
76
77 if (!comms->SendBytes(buf, buf_size)) {
78 LOG(ERROR) << "sandboxee_comms->SendBytes() failed";
79 return false;
80 }
81
82 if (!comms->RecvUint32(crc4)) {
83 LOG(ERROR) << "sandboxee_comms->RecvUint32(&crc4) failed";
84 return false;
85 }
86 return true;
87 }
88
89 } // namespace
90
main(int argc,char * argv[])91 int main(int argc, char* argv[]) {
92 absl::SetStderrThreshold(absl::LogSeverityAtLeast::kInfo);
93 absl::ParseCommandLine(argc, argv);
94 absl::InitializeLog();
95
96 if (absl::GetFlag(FLAGS_input).empty()) {
97 LOG(ERROR) << "Parameter --input required.";
98 return 1;
99 }
100
101 // Note: In your own code, use sapi::GetDataDependencyFilePath() instead.
102 const std::string path = sapi::internal::GetSapiDataDependencyFilePath(
103 "sandbox2/examples/crc4/crc4bin");
104 std::vector<std::string> args = {path};
105 if (absl::GetFlag(FLAGS_call_syscall_not_allowed)) {
106 args.push_back("-call_syscall_not_allowed");
107 }
108 std::vector<std::string> envs = {};
109 auto executor = std::make_unique<sandbox2::Executor>(path, args, envs);
110
111 executor
112 // Sandboxing is enabled by the binary itself (i.e. the crc4bin is capable
113 // of enabling sandboxing on its own).
114 ->set_enable_sandbox_before_exec(false)
115 .limits()
116 // Kill sandboxed processes with a signal (SIGXFSZ) if it writes more than
117 // these many bytes to the file-system.
118 ->set_rlimit_fsize(1024)
119 .set_rlimit_cpu(60) // The CPU time limit in seconds.
120 .set_walltime_limit(absl::Seconds(5));
121
122 sandbox2::Sandbox2 s2(std::move(executor), GetPolicy());
123
124 // Let the sandboxee run.
125 if (!s2.RunAsync()) {
126 sandbox2::Result result = s2.AwaitResult();
127 LOG(ERROR) << "RunAsync failed: " << result.ToString();
128 return 2;
129 }
130
131 sandbox2::Comms* comms = s2.comms();
132
133 uint32_t crc4;
134 if (!SandboxedCRC4(comms, &crc4)) {
135 LOG(ERROR) << "GetCRC4 failed";
136 if (!s2.IsTerminated()) {
137 // Kill the sandboxee, because failure to receive the data over the Comms
138 // channel doesn't automatically mean that the sandboxee itself had
139 // already finished. The final reason will not be overwritten, so if
140 // sandboxee finished because of e.g. timeout, the TIMEOUT reason will be
141 // reported.
142 LOG(INFO) << "Killing sandboxee";
143 s2.Kill();
144 }
145 }
146
147 sandbox2::Result result = s2.AwaitResult();
148 if (result.final_status() != sandbox2::Result::OK) {
149 LOG(ERROR) << "Sandbox error: " << result.ToString();
150 return 3; // e.g. sandbox violation, signal (sigsegv)
151 }
152 auto code = result.reason_code();
153 if (code) {
154 LOG(ERROR) << "Sandboxee exited with non-zero: " << code;
155 return 4; // e.g. normal child error
156 }
157 LOG(INFO) << "Sandboxee finished: " << result.ToString();
158 printf("0x%08x\n", crc4);
159 return EXIT_SUCCESS;
160 }
161