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 network binary.
16
17 #include <arpa/inet.h>
18 #include <netinet/in.h>
19 #include <sys/socket.h>
20 #include <syscall.h>
21 #include <unistd.h>
22
23 #include <cstdlib>
24 #include <memory>
25 #include <string>
26 #include <utility>
27 #include <vector>
28
29 #include "absl/flags/parse.h"
30 #include "absl/log/globals.h"
31 #include "absl/log/initialize.h"
32 #include "absl/log/log.h"
33 #include "absl/base/log_severity.h"
34 #include "absl/status/statusor.h"
35 #include "absl/strings/string_view.h"
36 #include "absl/time/time.h"
37 #include "sandboxed_api/config.h"
38 #include "sandboxed_api/sandbox2/comms.h"
39 #include "sandboxed_api/sandbox2/executor.h"
40 #include "sandboxed_api/sandbox2/network_proxy/testing.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/util/runfiles.h"
46
47 namespace {
48
GetPolicy(absl::string_view sandboxee_path)49 std::unique_ptr<sandbox2::Policy> GetPolicy(absl::string_view sandboxee_path) {
50 return sandbox2::PolicyBuilder()
51 .AllowExit()
52 .AllowMmapWithoutExec()
53 .AllowRead()
54 .AllowWrite()
55 .AllowSyscall(__NR_close)
56 .AllowSyscall(__NR_recvmsg) // RecvFD
57 .AllowSyscall(__NR_sendto) // send
58 .AllowStat() // printf,puts
59 .AddLibrariesForBinary(sandboxee_path)
60 .AllowTcMalloc()
61 .BuildOrDie();
62 }
63
ConnectToServer(int port)64 int ConnectToServer(int port) {
65 int s = socket(AF_INET6, SOCK_STREAM, 0);
66 if (s < 0) {
67 PLOG(ERROR) << "socket() failed";
68 return -1;
69 }
70
71 struct sockaddr_in6 saddr {};
72 saddr.sin6_family = AF_INET6;
73 saddr.sin6_port = htons(port);
74
75 int err = inet_pton(AF_INET6, "::1", &saddr.sin6_addr);
76 if (err == 0) {
77 LOG(ERROR) << "inet_pton() failed";
78 close(s);
79 return -1;
80 }
81 if (err == -1) {
82 PLOG(ERROR) << "inet_pton() failed";
83 close(s);
84 return -1;
85 }
86
87 err = connect(s, reinterpret_cast<const struct sockaddr*>(&saddr),
88 sizeof(saddr));
89 if (err != 0) {
90 LOG(ERROR) << "connect() failed";
91 close(s);
92 return -1;
93 }
94
95 LOG(INFO) << "Connected to the server";
96 return s;
97 }
98
HandleSandboxee(sandbox2::Comms * comms,int port)99 bool HandleSandboxee(sandbox2::Comms* comms, int port) {
100 // Connect to the server and pass the file descriptor to sandboxee.
101 int client = ConnectToServer(port);
102 if (client <= 0) {
103 LOG(ERROR) << "connect_to_server() failed";
104 return false;
105 }
106
107 if (!comms->SendFD(client)) {
108 LOG(ERROR) << "sandboxee_comms->SendFD(client) failed";
109 close(client);
110 return false;
111 }
112 close(client);
113 return true;
114 }
115
116 } // namespace
117
main(int argc,char * argv[])118 int main(int argc, char* argv[]) {
119 // This test is incompatible with sanitizers.
120 // The `SKIP_SANITIZERS_AND_COVERAGE` macro won't work for us here since we
121 // need to return something.
122 if constexpr (sapi::sanitizers::IsAny()) {
123 return EXIT_SUCCESS;
124 }
125
126 absl::SetStderrThreshold(absl::LogSeverityAtLeast::kInfo);
127 absl::ParseCommandLine(argc, argv);
128 absl::InitializeLog();
129
130 absl::StatusOr<std::unique_ptr<sandbox2::NetworkProxyTestServer>> server =
131 sandbox2::NetworkProxyTestServer::Start();
132 if (!server.ok()) {
133 LOG(ERROR) << server.status();
134 return EXIT_FAILURE;
135 }
136
137 // Note: In your own code, use sapi::GetDataDependencyFilePath() instead.
138 const std::string path = sapi::internal::GetSapiDataDependencyFilePath(
139 "sandbox2/examples/network/network_bin");
140 std::vector<std::string> args = {path};
141 std::vector<std::string> envs = {};
142
143 auto executor = std::make_unique<sandbox2::Executor>(path, args, envs);
144 executor
145 // Sandboxing is enabled by the binary itself (i.e. the crc4bin is capable
146 // of enabling sandboxing on its own).
147 ->set_enable_sandbox_before_exec(false)
148 // Set cwd to / to get rids of warnings connected with file namespace.
149 .set_cwd("/");
150
151 executor
152 ->limits()
153 // Kill sandboxed processes with a signal (SIGXFSZ) if it writes more than
154 // these many bytes to the file-system.
155 ->set_rlimit_fsize(10000)
156 .set_rlimit_cpu(100) // The CPU time limit in seconds
157 .set_walltime_limit(absl::Seconds(100));
158
159 auto policy = GetPolicy(path);
160 sandbox2::Sandbox2 s2(std::move(executor), std::move(policy));
161 auto* comms = s2.comms();
162
163 // Let the sandboxee run.
164 if (!s2.RunAsync()) {
165 auto result = s2.AwaitResult();
166 LOG(ERROR) << "RunAsync failed: " << result.ToString();
167 return 2;
168 }
169
170 if (!HandleSandboxee(comms, (*server)->port())) {
171 if (!s2.IsTerminated()) {
172 // Kill the sandboxee, because failure to receive the data over the Comms
173 // channel doesn't automatically mean that the sandboxee itself had
174 // already finished. The final reason will not be overwritten, so if
175 // sandboxee finished because of e.g. timeout, the TIMEOUT reason will be
176 // reported.
177 LOG(INFO) << "Killing sandboxee";
178 s2.Kill();
179 }
180 }
181
182 auto result = s2.AwaitResult();
183 if (result.final_status() != sandbox2::Result::OK) {
184 LOG(ERROR) << "Sandbox error: " << result.ToString();
185 return 3; // e.g. sandbox violation, signal (sigsegv).
186 }
187 auto code = result.reason_code();
188 if (code) {
189 LOG(ERROR) << "Sandboxee exited with non-zero: " << code;
190 return 4; // e.g. normal child error.
191 }
192 LOG(INFO) << "Sandboxee finished: " << result.ToString();
193 return EXIT_SUCCESS;
194 }
195