1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "host/commands/process_sandboxer/sandbox_manager.h"
17 
18 #include <fcntl.h>
19 #include <linux/sched.h>
20 #include <signal.h>
21 #include <sys/eventfd.h>
22 #include <sys/prctl.h>
23 #include <sys/signalfd.h>
24 #include <sys/socket.h>
25 #include <sys/syscall.h>
26 #include <sys/un.h>
27 #include <sys/wait.h>
28 #include <unistd.h>
29 
30 #include <memory>
31 #include <sstream>
32 #include <thread>
33 #include <utility>
34 
35 #include <absl/functional/bind_front.h>
36 #include <absl/log/log.h>
37 #include <absl/log/vlog_is_on.h>
38 #include <absl/memory/memory.h>
39 #include <absl/random/bit_gen_ref.h>
40 #include <absl/random/uniform_int_distribution.h>
41 #include <absl/status/status.h>
42 #include <absl/status/statusor.h>
43 #include <absl/strings/numbers.h>
44 #include <absl/strings/str_cat.h>
45 #include <absl/strings/str_format.h>
46 #include <absl/strings/str_join.h>
47 #include <absl/types/span.h>
48 #pragma clang diagnostic push
49 #pragma clang diagnostic ignored "-Wunused-parameter"
50 #include <sandboxed_api/sandbox2/executor.h>
51 #include <sandboxed_api/sandbox2/policy.h>
52 #include <sandboxed_api/sandbox2/sandbox2.h>
53 #include <sandboxed_api/sandbox2/util.h>
54 #pragma clang diagnostic pop
55 
56 #include "host/commands/process_sandboxer/credentialed_unix_server.h"
57 #include "host/commands/process_sandboxer/filesystem.h"
58 #include "host/commands/process_sandboxer/pidfd.h"
59 #include "host/commands/process_sandboxer/policies.h"
60 #include "host/commands/process_sandboxer/poll_callback.h"
61 #include "host/commands/process_sandboxer/proxy_common.h"
62 
63 namespace cuttlefish::process_sandboxer {
64 
65 using sandbox2::Executor;
66 using sandbox2::Policy;
67 using sandbox2::Sandbox2;
68 using sandbox2::Syscall;
69 using sandbox2::util::GetProgName;
70 
71 namespace {
72 
ServerSocketOutsidePath(std::string_view runtime_dir)73 std::string ServerSocketOutsidePath(std::string_view runtime_dir) {
74   return JoinPath(runtime_dir, "/", "server.sock");
75 }
76 
77 }  // namespace
78 
79 class SandboxManager::ProcessNoSandbox : public SandboxManager::ManagedProcess {
80  public:
ProcessNoSandbox(int client_fd,PidFd pid_fd)81   ProcessNoSandbox(int client_fd, PidFd pid_fd)
82       : client_fd_(client_fd), pid_fd_(std::move(pid_fd)) {}
~ProcessNoSandbox()83   ~ProcessNoSandbox() {
84     auto halt = pid_fd_.HaltHierarchy();
85     if (!halt.ok()) {
86       LOG(ERROR) << "Failed to halt children: " << halt.ToString();
87     }
88   }
89 
ClientFd() const90   std::optional<int> ClientFd() const override { return client_fd_; }
PollFd() const91   int PollFd() const override { return pid_fd_.Get(); }
92 
ExitCode()93   absl::StatusOr<uintptr_t> ExitCode() override {
94     siginfo_t infop;
95     idtype_t id_type = (idtype_t)3;  // P_PIDFD
96     if (waitid(id_type, pid_fd_.Get(), &infop, WEXITED | WNOWAIT) < 0) {
97       return absl::ErrnoToStatus(errno, "`waitid` failed");
98     }
99     switch (infop.si_code) {
100       case CLD_EXITED:
101         return infop.si_status;
102       case CLD_DUMPED:
103       case CLD_KILLED:
104         LOG(ERROR) << "Child killed by signal " << infop.si_code;
105         return 255;
106       default:
107         LOG(ERROR) << "Unexpected si_code: " << infop.si_code;
108         return 255;
109     }
110   }
111 
112  private:
113   int client_fd_;
114   PidFd pid_fd_;
115 };
116 
117 class SandboxManager::SandboxedProcess : public SandboxManager::ManagedProcess {
118  public:
SandboxedProcess(std::optional<int> client_fd,UniqueFd event_fd,std::unique_ptr<Sandbox2> sandbox)119   SandboxedProcess(std::optional<int> client_fd, UniqueFd event_fd,
120                    std::unique_ptr<Sandbox2> sandbox)
121       : client_fd_(client_fd),
122         event_fd_(std::move(event_fd)),
123         sandbox_(std::move(sandbox)) {
124     waiter_thread_ = std::thread([this]() { WaitForExit(); });
125   }
~SandboxedProcess()126   ~SandboxedProcess() override {
127     sandbox_->Kill();
128     waiter_thread_.join();
129     auto res = sandbox_->AwaitResult().ToStatus();
130     if (!res.ok()) {
131       LOG(ERROR) << "Issue in closing sandbox: '" << res.ToString() << "'";
132     }
133   }
134 
ClientFd() const135   std::optional<int> ClientFd() const override { return client_fd_; }
PollFd() const136   int PollFd() const override { return event_fd_.Get(); }
137 
ExitCode()138   absl::StatusOr<uintptr_t> ExitCode() override {
139     return sandbox_->AwaitResult().reason_code();
140   }
141 
142  private:
WaitForExit()143   void WaitForExit() {
144     sandbox_->AwaitResult().IgnoreResult();
145     uint64_t buf = 1;
146     if (write(event_fd_.Get(), &buf, sizeof(buf)) < 0) {
147       PLOG(ERROR) << "Failed to write to eventfd";
148     }
149   }
150 
151   std::optional<int> client_fd_;
152   UniqueFd event_fd_;
153   std::thread waiter_thread_;
154   std::unique_ptr<Sandbox2> sandbox_;
155 };
156 
RandomString(absl::BitGenRef gen,std::size_t size)157 std::string RandomString(absl::BitGenRef gen, std::size_t size) {
158   std::stringstream output;
159   absl::uniform_int_distribution<char> distribution;
160   for (std::size_t i = 0; i < size; i++) {
161     output << distribution(gen);
162   }
163   return output.str();
164 }
165 
166 class SandboxManager::SocketClient {
167  public:
SocketClient(SandboxManager & manager,UniqueFd client_fd)168   SocketClient(SandboxManager& manager, UniqueFd client_fd)
169       : manager_(manager), client_fd_(std::move(client_fd)) {}
170   SocketClient(SocketClient&) = delete;
171 
ClientFd() const172   int ClientFd() const { return client_fd_.Get(); }
173 
HandleMessage()174   absl::Status HandleMessage() {
175     auto message_status = Message::RecvFrom(client_fd_.Get());
176     if (!message_status.ok()) {
177       return message_status.status();
178     }
179     auto creds_status = UpdateCredentials(message_status->Credentials());
180     if (!creds_status.ok()) {
181       return creds_status;
182     }
183 
184     /* This handshake process is to reliably build a `pidfd` based on the pid
185      * supplied in the process `ucreds`, through the following steps:
186      * 1. Proxy process opens a socket and sends an opening message.
187      * 2. Server receives opening message with a kernel-validated `ucreds`
188      *    containing the outside-sandbox pid.
189      * 3. Server opens a pidfd matching this pid.
190      * 4. Server sends a message to the client with some unique data.
191      * 5. Client responds with the unique data.
192      * 6. Server validates the unique data and credentials match.
193      * 7. Server launches a possible sandboxed subprocess based on the pidfd and
194      *    /proc/{pid}/
195      *
196      * Step 5 builds confidence that the pidfd opened in step 3 still
197      * corresponds to the client sending messages on the client socket. The
198      * pidfd and /proc/{pid} data provide everything necessary to launch the
199      * subprocess.
200      */
201     auto& message = message_status->Data();
202     switch (client_state_) {
203       case ClientState::kInitial: {
204         if (message != kHandshakeBegin) {
205           std::string err =
206               absl::StrFormat("'%v' != '%v'", kHandshakeBegin, message);
207           return absl::InternalError(err);
208         }
209         pingback_ = RandomString(manager_.bit_gen_, 32);
210         absl::StatusOr<std::size_t> stat =
211             SendStringMsg(client_fd_.Get(), pingback_);
212         if (stat.ok()) {
213           client_state_ = ClientState::kIgnoredFd;
214         }
215         return stat.status();
216       }
217       case ClientState::kIgnoredFd:
218         if (!absl::SimpleAtoi(message, &ignored_fd_)) {
219           std::string error =
220               absl::StrFormat("Expected integer, got '%v'", message);
221           return absl::InternalError(error);
222         }
223         client_state_ = ClientState::kPingback;
224         return absl::OkStatus();
225       case ClientState::kPingback: {
226         if (message != pingback_) {
227           std::string err =
228               absl::StrFormat("Incorrect '%v' != '%v'", message, pingback_);
229           return absl::InternalError(err);
230         }
231         client_state_ = ClientState::kWaitingForExit;
232         return LaunchProcess();
233       }
234       case ClientState::kWaitingForExit:
235         return absl::InternalError("No messages allowed");
236     }
237   }
238 
SendExitCode(int code)239   absl::Status SendExitCode(int code) {
240     auto send_exit_status = SendStringMsg(client_fd_.Get(), "exit");
241     if (!send_exit_status.ok()) {
242       return send_exit_status.status();
243     }
244 
245     return SendStringMsg(client_fd_.Get(), std::to_string(code)).status();
246   }
247 
248  private:
249   enum class ClientState { kInitial, kIgnoredFd, kPingback, kWaitingForExit };
250 
UpdateCredentials(const std::optional<ucred> & credentials)251   absl::Status UpdateCredentials(const std::optional<ucred>& credentials) {
252     if (!credentials) {
253       return absl::InvalidArgumentError("no creds");
254     } else if (!credentials_) {
255       credentials_ = credentials;
256     } else if (credentials_->pid != credentials->pid) {
257       std::string err = absl::StrFormat("pid went from '%d' to '%d'",
258                                         credentials_->pid, credentials->pid);
259       return absl::PermissionDeniedError(err);
260     } else if (credentials_->uid != credentials->uid) {
261       return absl::PermissionDeniedError("uid changed");
262     } else if (credentials_->gid != credentials->gid) {
263       return absl::PermissionDeniedError("gid changed");
264     }
265     if (!pid_fd_) {
266       absl::StatusOr<PidFd> pid_fd =
267           PidFd::FromRunningProcess(credentials_->pid);
268       if (!pid_fd.ok()) {
269         return pid_fd.status();
270       }
271       pid_fd_ = std::move(*pid_fd);
272     }
273     return absl::OkStatus();
274   }
275 
LaunchProcess()276   absl::Status LaunchProcess() {
277     if (!pid_fd_) {
278       return absl::InternalError("missing pid_fd_");
279     }
280     absl::StatusOr<std::vector<std::string>> argv = pid_fd_->Argv();
281     if (!argv.ok()) {
282       return argv.status();
283     }
284     if ((*argv)[0] == "openssl") {
285       (*argv)[0] = "/usr/bin/openssl";
286     }
287     absl::StatusOr<std::vector<std::pair<UniqueFd, int>>> fds =
288         pid_fd_->AllFds();
289     if (!fds.ok()) {
290       return fds.status();
291     }
292     absl::StatusOr<std::vector<std::string>> env = pid_fd_->Env();
293     if (!env.ok()) {
294       return env.status();
295     }
296     fds->erase(std::remove_if(fds->begin(), fds->end(), [this](auto& arg) {
297       return arg.second == ignored_fd_;
298     }));
299     return manager_.RunProcess(client_fd_.Get(), std::move(*argv),
300                                std::move(*fds), *env);
301   }
302 
303   SandboxManager& manager_;
304   UniqueFd client_fd_;
305   std::optional<ucred> credentials_;
306   std::optional<PidFd> pid_fd_;
307 
308   ClientState client_state_ = ClientState::kInitial;
309   std::string pingback_;
310   int ignored_fd_ = -1;
311 };
312 
SandboxManager(HostInfo host_info,std::string runtime_dir,SignalFd signals,CredentialedUnixServer server)313 SandboxManager::SandboxManager(HostInfo host_info, std::string runtime_dir,
314                                SignalFd signals, CredentialedUnixServer server)
315     : host_info_(std::move(host_info)),
316       runtime_dir_(std::move(runtime_dir)),
317       signals_(std::move(signals)),
318       server_(std::move(server)) {}
319 
Create(HostInfo host_info)320 absl::StatusOr<std::unique_ptr<SandboxManager>> SandboxManager::Create(
321     HostInfo host_info) {
322   std::string runtime_dir =
323       absl::StrFormat("/tmp/sandbox_manager.%u.XXXXXX", getpid());
324   if (mkdtemp(runtime_dir.data()) == nullptr) {
325     return absl::ErrnoToStatus(errno, "mkdtemp failed");
326   }
327   VLOG(1) << "Created temporary directory '" << runtime_dir << "'";
328 
329   absl::StatusOr<SignalFd> signals = SignalFd::AllExceptSigChld();
330   if (!signals.ok()) {
331     return signals.status();
332   }
333 
334   absl::StatusOr<CredentialedUnixServer> server =
335       CredentialedUnixServer::Open(ServerSocketOutsidePath(runtime_dir));
336   if (!server.ok()) {
337     return server.status();
338   }
339 
340   return absl::WrapUnique(
341       new SandboxManager(std::move(host_info), std::move(runtime_dir),
342                          std::move(*signals), std::move(*server)));
343 }
344 
~SandboxManager()345 SandboxManager::~SandboxManager() {
346   VLOG(1) << "Sandbox shutting down";
347   if (!runtime_dir_.empty()) {
348     if (unlink(ServerSocketOutsidePath(runtime_dir_).c_str()) < 0) {
349       PLOG(ERROR) << "`unlink` failed";
350     }
351     if (rmdir(runtime_dir_.c_str()) < 0) {
352       PLOG(ERROR) << "Failed to remove '" << runtime_dir_ << "'";
353     }
354   }
355 }
356 
RunProcess(std::optional<int> client_fd,absl::Span<const std::string> argv,std::vector<std::pair<UniqueFd,int>> fds,absl::Span<const std::string> env)357 absl::Status SandboxManager::RunProcess(
358     std::optional<int> client_fd, absl::Span<const std::string> argv,
359     std::vector<std::pair<UniqueFd, int>> fds,
360     absl::Span<const std::string> env) {
361   if (argv.empty()) {
362     return absl::InvalidArgumentError("Not enough arguments");
363   }
364   bool stdio_mapped[3] = {false, false, false};
365   for (const auto& [input_fd, target_fd] : fds) {
366     if (0 <= target_fd && target_fd <= 2) {
367       stdio_mapped[target_fd] = true;
368     }
369   }
370   // If stdio is not filled in, file descriptors opened by the target process
371   // may occupy the standard stdio positions. This can cause unexpected
372   for (int i = 0; i <= 2; i++) {
373     if (stdio_mapped[i]) {
374       continue;
375     }
376     auto& [stdio_dup, stdio] = fds.emplace_back(dup(i), i);
377     if (stdio_dup.Get() < 0) {
378       return absl::ErrnoToStatus(errno, "Failed to `dup` stdio descriptor");
379     }
380   }
381   std::string exe = CleanPath(argv[0]);
382   std::unique_ptr<Policy> policy = PolicyForExecutable(
383       host_info_, ServerSocketOutsidePath(runtime_dir_), exe);
384   if (policy) {
385     return RunSandboxedProcess(client_fd, argv, std::move(fds), env,
386                                std::move(policy));
387   } else {
388     return RunProcessNoSandbox(client_fd, argv, std::move(fds), env);
389   }
390 }
391 
RunSandboxedProcess(std::optional<int> client_fd,absl::Span<const std::string> argv,std::vector<std::pair<UniqueFd,int>> fds,absl::Span<const std::string> env,std::unique_ptr<Policy> policy)392 absl::Status SandboxManager::RunSandboxedProcess(
393     std::optional<int> client_fd, absl::Span<const std::string> argv,
394     std::vector<std::pair<UniqueFd, int>> fds,
395     absl::Span<const std::string> env, std::unique_ptr<Policy> policy) {
396   if (VLOG_IS_ON(1)) {
397     std::stringstream process_stream;
398     process_stream << "Launching executable with argv: [\n";
399     for (const auto& arg : argv) {
400       process_stream << "\t\"" << arg << "\",\n";
401     }
402     process_stream << "] with FD mapping: [\n";
403     for (const auto& [fd_in, fd_out] : fds) {
404       process_stream << '\t' << fd_in.Get() << " -> " << fd_out << ",\n";
405     }
406     process_stream << "]\n";
407     VLOG(1) << process_stream.str();
408   }
409 
410   std::string exe = CleanPath(argv[0]);
411   auto executor = std::make_unique<Executor>(exe, argv, env);
412   executor->set_cwd(host_info_.runtime_dir);
413 
414   // https://cs.android.com/android/platform/superproject/main/+/main:external/sandboxed-api/sandboxed_api/sandbox2/limits.h;l=116;drc=d451478e26c0352ecd6912461e867a1ae64b17f5
415   // Default is 120 seconds
416   executor->limits()->set_walltime_limit(absl::InfiniteDuration());
417   // Default is 1024 seconds
418   executor->limits()->set_rlimit_cpu(RLIM64_INFINITY);
419 
420   for (auto& [fd_outer, fd_inner] : fds) {
421     // Will close `fd_outer` in this process
422     executor->ipc()->MapFd(fd_outer.Release(), fd_inner);
423   }
424 
425   UniqueFd event_fd(eventfd(0, EFD_CLOEXEC));
426   if (event_fd.Get() < 0) {
427     return absl::ErrnoToStatus(errno, "`eventfd` failed");
428   }
429 
430   auto sbx = std::make_unique<Sandbox2>(std::move(executor), std::move(policy));
431   if (!sbx->RunAsync()) {
432     return sbx->AwaitResult().ToStatus();
433   }
434 
435   // A pidfd over the sandbox is another option, but there are two problems:
436   //
437   // 1. There's a race between launching the sandbox and opening the pidfd. If
438   // the sandboxed process exits too quickly, the monitor thread in sandbox2
439   // could reap it and another process could reuse the pid before `pidfd_open`
440   // runs. Sandbox2 could produce a pidfd itself using `CLONE_PIDFD`, but it
441   // does not do this at the time of writing.
442   //
443   // 2. The sandbox could outlive its top-level process. It's not clear to me if
444   // sandbox2 allows this in practice, but `AwaitResult` could theoretically
445   // wait on subprocesses of the original sandboxed process as well.
446   //
447   // To deal with these concerns, we use another thread blocked on AwaitResult
448   // that signals the eventfd when sandbox2 says the sandboxed process has
449   // exited.
450 
451   subprocesses_.emplace_back(
452       new SandboxedProcess(client_fd, std::move(event_fd), std::move(sbx)));
453 
454   return absl::OkStatus();
455 }
456 
RunProcessNoSandbox(std::optional<int> client_fd,absl::Span<const std::string> argv,std::vector<std::pair<UniqueFd,int>> fds,absl::Span<const std::string> env)457 absl::Status SandboxManager::RunProcessNoSandbox(
458     std::optional<int> client_fd, absl::Span<const std::string> argv,
459     std::vector<std::pair<UniqueFd, int>> fds,
460     absl::Span<const std::string> env) {
461   if (!client_fd) {
462     return absl::InvalidArgumentError("no client for unsandboxed process");
463   }
464 
465   absl::StatusOr<PidFd> fd = PidFd::LaunchSubprocess(argv, std::move(fds), env);
466   if (!fd.ok()) {
467     return fd.status();
468   }
469   subprocesses_.emplace_back(new ProcessNoSandbox(*client_fd, std::move(*fd)));
470 
471   return absl::OkStatus();
472 }
473 
Running() const474 bool SandboxManager::Running() const { return running_; }
475 
Iterate()476 absl::Status SandboxManager::Iterate() {
477   PollCallback poll_cb;
478 
479   poll_cb.Add(signals_.Fd(), bind_front(&SandboxManager::Signalled, this));
480   poll_cb.Add(server_.Fd(), bind_front(&SandboxManager::NewClient, this));
481 
482   for (auto it = subprocesses_.begin(); it != subprocesses_.end(); it++) {
483     int fd = (*it)->PollFd();
484     poll_cb.Add(fd, bind_front(&SandboxManager::ProcessExit, this, it));
485   }
486   for (auto it = clients_.begin(); it != clients_.end(); it++) {
487     int fd = (*it)->ClientFd();
488     poll_cb.Add(fd, bind_front(&SandboxManager::ClientMessage, this, it));
489   }
490 
491   return poll_cb.Poll();
492 }
493 
Signalled(short revents)494 absl::Status SandboxManager::Signalled(short revents) {
495   if (revents != POLLIN) {
496     running_ = false;
497     return absl::InternalError("signalfd exited");
498   }
499 
500   absl::StatusOr<signalfd_siginfo> info = signals_.ReadSignal();
501   if (!info.ok()) {
502     return info.status();
503   }
504   VLOG(1) << "Received signal with signo '" << info->ssi_signo << "'";
505 
506   switch (info->ssi_signo) {
507     case SIGHUP:
508     case SIGINT:
509     case SIGTERM:
510       LOG(INFO) << "Received signal '" << info->ssi_signo << "', exiting";
511       running_ = false;
512       return absl::OkStatus();
513     default:
514       std::string err = absl::StrCat("Unexpected signal ", info->ssi_signo);
515       return absl::InternalError(err);
516   }
517 }
518 
NewClient(short revents)519 absl::Status SandboxManager::NewClient(short revents) {
520   if (revents != POLLIN) {
521     running_ = false;
522     return absl::InternalError("server socket exited");
523   }
524   absl::StatusOr<UniqueFd> client = server_.AcceptClient();
525   if (!client.ok()) {
526     return client.status();
527   }
528   clients_.emplace_back(new SocketClient(*this, std::move(*client)));
529   return absl::OkStatus();
530 }
531 
ProcessExit(SandboxManager::SboxIter it,short revents)532 absl::Status SandboxManager::ProcessExit(SandboxManager::SboxIter it,
533                                          short revents) {
534   if ((*it)->ClientFd()) {
535     int client_fd = *(*it)->ClientFd();
536     for (auto& client : clients_) {
537       if (client->ClientFd() != client_fd) {
538         continue;
539       }
540       auto exit_code = (*it)->ExitCode();
541       if (!exit_code.ok()) {
542         LOG(ERROR) << exit_code.status();
543       }
544       // TODO(schuffelen): Forward more complete exit information
545       auto send_res = client->SendExitCode(exit_code.value_or(254));
546       if (!send_res.ok()) {
547         return send_res;
548       }
549     }
550   }
551   subprocesses_.erase(it);
552   if (subprocesses_.empty()) {
553     running_ = false;
554   }
555   static constexpr char kErr[] = "eventfd exited";
556   return revents == POLLIN ? absl::OkStatus() : absl::InternalError(kErr);
557 }
558 
ClientMessage(SandboxManager::ClientIter it,short rev)559 absl::Status SandboxManager::ClientMessage(SandboxManager::ClientIter it,
560                                            short rev) {
561   if (rev == POLLIN) {
562     return (*it)->HandleMessage();
563   }
564   clients_.erase(it);
565   return absl::InternalError("client dropped file descriptor");
566 }
567 
568 }  // namespace cuttlefish::process_sandboxer
569