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