| // Copyright 2017 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifdef UNSAFE_BUFFERS_BUILD |
| // TODO(crbug.com/351564777): Remove this and convert code to safer constructs. |
| #pragma allow_unsafe_buffers |
| #endif |
| |
| #include "sandbox/mac/seatbelt_exec.h" |
| |
| #include <stdint.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include <limits> |
| #include <vector> |
| |
| #include "base/posix/eintr_wrapper.h" //nogncheck |
| #include "sandbox/mac/sandbox_logging.h" |
| #include "sandbox/mac/sandbox_serializer.h" |
| #include "sandbox/mac/seatbelt.h" |
| |
| namespace sandbox { |
| |
| namespace { |
| |
| struct ReadTraits { |
| using BufferType = uint8_t*; |
| static constexpr char kNameString[] = "read"; |
| static ssize_t Operate(int fd, BufferType buffer, size_t size) { |
| return read(fd, buffer, size); |
| } |
| }; |
| constexpr char ReadTraits::kNameString[]; |
| |
| struct WriteTraits { |
| using BufferType = const uint8_t*; |
| static constexpr char kNameString[] = "write"; |
| static ssize_t Operate(int fd, BufferType buffer, size_t size) { |
| return write(fd, buffer, size); |
| } |
| }; |
| constexpr char WriteTraits::kNameString[]; |
| |
| template <typename Traits> |
| bool ReadOrWrite(int fd, |
| const typename Traits::BufferType buffer, |
| const size_t size, |
| const char* operation_description) { |
| if (size > std::numeric_limits<ssize_t>::max()) { |
| logging::Error("request size is greater than ssize_t::max"); |
| return false; |
| } |
| |
| ssize_t bytes_to_transact = static_cast<ssize_t>(size); |
| |
| while (bytes_to_transact > 0) { |
| ssize_t offset = size - bytes_to_transact; |
| ssize_t transacted_bytes = |
| HANDLE_EINTR(Traits::Operate(fd, buffer + offset, bytes_to_transact)); |
| if (transacted_bytes < 0) { |
| logging::PError("SeatbeltExec: %s %s failed", operation_description, |
| Traits::kNameString); |
| return false; |
| } else if (transacted_bytes == 0) { |
| // A short read from the sender, perhaps the sender process died. |
| logging::Error("SeatbeltExec: %s %s failed", operation_description, |
| Traits::kNameString); |
| return false; |
| } |
| |
| bytes_to_transact -= transacted_bytes; |
| } |
| |
| return true; |
| } |
| |
| } // namespace |
| |
| namespace switches { |
| |
| const char kSeatbeltClient[] = "--seatbelt-client="; |
| |
| const char kSeatbeltClientName[] = "seatbelt-client"; |
| |
| } // namespace switches |
| |
| SeatbeltExecClient::SeatbeltExecClient() { |
| if (pipe(pipe_) != 0) |
| logging::PFatal("SeatbeltExecClient: pipe failed"); |
| } |
| |
| SeatbeltExecClient::~SeatbeltExecClient() { |
| if (pipe_[0] != -1) |
| IGNORE_EINTR(close(pipe_[0])); |
| if (pipe_[1] != -1) |
| IGNORE_EINTR(close(pipe_[1])); |
| } |
| |
| int SeatbeltExecClient::GetReadFD() { |
| return pipe_[0]; |
| } |
| |
| bool SeatbeltExecClient::SendPolicy(const std::string& serialized_policy) { |
| IGNORE_EINTR(close(pipe_[0])); |
| pipe_[0] = -1; |
| |
| if (!WriteString(serialized_policy)) { |
| return false; |
| } |
| |
| IGNORE_EINTR(close(pipe_[1])); |
| pipe_[1] = -1; |
| |
| return true; |
| } |
| |
| bool SeatbeltExecClient::WriteString(const std::string& str) { |
| uint64_t str_len = static_cast<uint64_t>(str.size()); |
| if (!ReadOrWrite<WriteTraits>(pipe_[1], reinterpret_cast<uint8_t*>(&str_len), |
| sizeof(str_len), "buffer length")) { |
| return false; |
| } |
| |
| if (!ReadOrWrite<WriteTraits>(pipe_[1], |
| reinterpret_cast<const uint8_t*>(&str[0]), |
| str_len, "buffer")) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| SeatbeltExecServer::SeatbeltExecServer(int fd) : fd_(fd) {} |
| |
| SeatbeltExecServer::~SeatbeltExecServer() { |
| close(fd_); |
| } |
| |
| sandbox::SeatbeltExecServer::CreateFromArgumentsResult:: |
| CreateFromArgumentsResult() = default; |
| sandbox::SeatbeltExecServer::CreateFromArgumentsResult:: |
| CreateFromArgumentsResult(CreateFromArgumentsResult&&) = default; |
| sandbox::SeatbeltExecServer::CreateFromArgumentsResult:: |
| ~CreateFromArgumentsResult() = default; |
| |
| // static |
| sandbox::SeatbeltExecServer::CreateFromArgumentsResult |
| SeatbeltExecServer::CreateFromArguments(const char* executable_path, |
| int argc, |
| const char* const* argv) { |
| CreateFromArgumentsResult result; |
| int seatbelt_client_fd = -1; |
| for (int i = 1; i < argc; ++i) { |
| if (strncmp(argv[i], switches::kSeatbeltClient, |
| strlen(switches::kSeatbeltClient)) == 0) { |
| result.sandbox_required = true; |
| std::string arg(argv[i]); |
| std::string fd_string = arg.substr(strlen(switches::kSeatbeltClient)); |
| seatbelt_client_fd = std::stoi(fd_string); |
| } |
| } |
| |
| if (!result.sandbox_required) |
| return result; |
| |
| if (seatbelt_client_fd < 0) { |
| logging::Error("Must pass a valid file descriptor to %s", |
| switches::kSeatbeltClient); |
| return result; |
| } |
| |
| result.server.reset(new SeatbeltExecServer(seatbelt_client_fd)); |
| return result; |
| } |
| |
| bool SeatbeltExecServer::InitializeSandbox() { |
| std::string policy_string; |
| if (!ReadString(&policy_string)) |
| return false; |
| |
| return SandboxSerializer::ApplySerializedPolicy(policy_string); |
| } |
| |
| bool SeatbeltExecServer::ReadString(std::string* str) { |
| uint64_t buf_len = 0; |
| if (!ReadOrWrite<ReadTraits>(fd_, reinterpret_cast<uint8_t*>(&buf_len), |
| sizeof(buf_len), "buffer length")) { |
| return false; |
| } |
| |
| str->resize(buf_len); |
| |
| if (!ReadOrWrite<ReadTraits>(fd_, reinterpret_cast<uint8_t*>(&(*str)[0]), |
| buf_len, "buffer")) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } // namespace sandbox |