| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "sandbox/mac/seatbelt_exec.h" |
| |
| #include <fcntl.h> |
| #include <inttypes.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <sys/socket.h> |
| #include <sys/uio.h> |
| #include <unistd.h> |
| |
| #include <vector> |
| |
| #include "base/macros.h" |
| #include "base/posix/eintr_wrapper.h" |
| #include "sandbox/mac/sandbox_logging.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) { |
| 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("%s failed", 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])); |
| } |
| |
| bool SeatbeltExecClient::SetBooleanParameter(const std::string& key, |
| bool value) { |
| google::protobuf::MapPair<std::string, std::string> pair( |
| key, value ? "TRUE" : "FALSE"); |
| return policy_.mutable_params()->insert(pair).second; |
| } |
| |
| bool SeatbeltExecClient::SetParameter(const std::string& key, |
| const std::string& value) { |
| google::protobuf::MapPair<std::string, std::string> pair(key, value); |
| return policy_.mutable_params()->insert(pair).second; |
| } |
| |
| void SeatbeltExecClient::SetProfile(const std::string& policy) { |
| policy_.set_profile(policy); |
| } |
| |
| int SeatbeltExecClient::GetReadFD() { |
| return pipe_[0]; |
| } |
| |
| bool SeatbeltExecClient::SendProfile() { |
| IGNORE_EINTR(close(pipe_[0])); |
| pipe_[0] = -1; |
| |
| std::string serialized_protobuf; |
| if (!policy_.SerializeToString(&serialized_protobuf)) { |
| logging::Error("SeatbeltExecClient: Serializing the profile failed."); |
| return false; |
| } |
| |
| if (!WriteString(serialized_protobuf)) { |
| logging::Error( |
| "SeatbeltExecClient: Writing the serialized profile failed."); |
| 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))) { |
| logging::Error("SeatbeltExecClient: write buffer length failed."); |
| return false; |
| } |
| |
| if (!ReadOrWrite<WriteTraits>( |
| pipe_[1], reinterpret_cast<const uint8_t*>(&str[0]), str_len)) { |
| logging::Error("SeatbeltExecClient: write buffer failed."); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| SeatbeltExecServer::SeatbeltExecServer(int fd) : fd_(fd), extra_params_() {} |
| |
| 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, |
| char** 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; |
| } |
| |
| char full_exec_path[MAXPATHLEN]; |
| if (realpath(executable_path, full_exec_path) == NULL) { |
| logging::PError("realpath"); |
| return result; |
| } |
| |
| auto server = std::make_unique<SeatbeltExecServer>(seatbelt_client_fd); |
| // These parameters are provided for every profile to use. |
| if (!server->SetParameter("EXECUTABLE_PATH", full_exec_path) || |
| !server->SetParameter("CURRENT_PID", std::to_string(getpid()))) { |
| logging::Error("Failed to set up parameters for sandbox."); |
| return result; |
| } |
| |
| result.server = std::move(server); |
| return result; |
| } |
| |
| bool SeatbeltExecServer::InitializeSandbox() { |
| std::string policy_string; |
| if (!ReadString(&policy_string)) |
| return false; |
| |
| mac::SandboxPolicy policy; |
| if (!policy.ParseFromString(policy_string)) { |
| logging::Error("SeatbeltExecServer: ParseFromString failed"); |
| return false; |
| } |
| |
| return ApplySandboxProfile(policy); |
| } |
| |
| bool SeatbeltExecServer::ApplySandboxProfile(const mac::SandboxPolicy& policy) { |
| std::vector<const char*> weak_params; |
| for (const auto& pair : policy.params()) { |
| weak_params.push_back(pair.first.c_str()); |
| weak_params.push_back(pair.second.c_str()); |
| } |
| for (const auto& pair : extra_params_) { |
| weak_params.push_back(pair.first.c_str()); |
| weak_params.push_back(pair.second.c_str()); |
| } |
| weak_params.push_back(nullptr); |
| |
| char* error = nullptr; |
| int rv = Seatbelt::InitWithParams(policy.profile().c_str(), 0, |
| weak_params.data(), &error); |
| if (error) { |
| logging::Error("SeatbeltExecServer: Failed to initialize sandbox: %d %s", |
| rv, error); |
| Seatbelt::FreeError(error); |
| return false; |
| } |
| |
| return rv == 0; |
| } |
| |
| bool SeatbeltExecServer::ReadString(std::string* str) { |
| uint64_t buf_len = 0; |
| if (!ReadOrWrite<ReadTraits>(fd_, reinterpret_cast<uint8_t*>(&buf_len), |
| sizeof(buf_len))) { |
| logging::Error("SeatbeltExecServer: failed to read buffer length."); |
| return false; |
| } |
| |
| str->resize(buf_len); |
| |
| if (!ReadOrWrite<ReadTraits>(fd_, reinterpret_cast<uint8_t*>(&(*str)[0]), |
| buf_len)) { |
| logging::Error("SeatbeltExecServer: failed to read buffer."); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool SeatbeltExecServer::SetParameter(const std::string& key, |
| const std::string& value) { |
| return extra_params_.insert(std::make_pair(key, value)).second; |
| } |
| |
| } // namespace sandbox |