1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sandbox/linux/suid/client/setuid_sandbox_client.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <optional>
#include <string>
#include <utility>
#include "base/environment.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/posix/eintr_wrapper.h"
#include "base/strings/cstring_view.h"
#include "base/strings/string_number_conversions.h"
#include "sandbox/linux/suid/common/sandbox.h"
namespace {
bool IsFileSystemAccessDenied() {
// We would rather check "/" instead of "/proc/self/exe" here, but
// that gives false positives when running as root. See
// https://codereview.chromium.org/2578483002/#msg3
base::ScopedFD proc_self_exe(HANDLE_EINTR(open("/proc/self/exe", O_RDONLY)));
return !proc_self_exe.is_valid();
}
int GetHelperApi(base::Environment* env) {
std::optional<std::string> api_string =
env->GetVar(sandbox::kSandboxEnvironmentApiProvides);
int api_number = 0; // Assume API version 0 if no environment was found.
if (api_string.has_value() &&
!base::StringToInt(api_string.value(), &api_number)) {
// It's an error if we could not convert the API number.
api_number = -1;
}
return api_number;
}
// Convert |var_name| from the environment |env| to an int.
// Return -1 if the variable does not exist or the value cannot be converted.
int EnvToInt(base::Environment* env, base::cstring_view var_name) {
std::string var_string = env->GetVar(var_name).value_or(std::string());
int var_value = -1;
if (!var_string.empty() && !base::StringToInt(var_string, &var_value)) {
var_value = -1;
}
return var_value;
}
pid_t GetHelperPID(base::Environment* env) {
return EnvToInt(env, sandbox::kSandboxHelperPidEnvironmentVarName);
}
// Get the IPC file descriptor used to communicate with the setuid helper.
int GetIPCDescriptor(base::Environment* env) {
return EnvToInt(env, sandbox::kSandboxDescriptorEnvironmentVarName);
}
} // namespace
namespace sandbox {
std::unique_ptr<SetuidSandboxClient> SetuidSandboxClient::Create() {
// Private constructor.
return base::WrapUnique(new SetuidSandboxClient(base::Environment::Create()));
}
SetuidSandboxClient::SetuidSandboxClient(std::unique_ptr<base::Environment> env)
: env_(std::move(env)) {
DCHECK(env_);
}
SetuidSandboxClient::~SetuidSandboxClient() = default;
void SetuidSandboxClient::CloseDummyFile() {
// When we're launched through the setuid sandbox, SetupLaunchOptions
// arranges for kZygoteIdFd to be a dummy file descriptor to satisfy an
// ancient setuid sandbox ABI requirement. However, the descriptor is no
// longer needed, so we can simply close it right away now.
CHECK(IsSuidSandboxChild());
// Sanity check that kZygoteIdFd refers to a pipe.
struct stat st;
PCHECK(0 == fstat(kZygoteIdFd, &st));
CHECK(S_ISFIFO(st.st_mode));
PCHECK(0 == IGNORE_EINTR(close(kZygoteIdFd)));
}
bool SetuidSandboxClient::ChrootMe() {
int ipc_fd = GetIPCDescriptor(env_.get());
if (ipc_fd < 0) {
LOG(ERROR) << "Failed to obtain the sandbox IPC descriptor";
return false;
}
if (HANDLE_EINTR(write(ipc_fd, &kMsgChrootMe, 1)) != 1) {
PLOG(ERROR) << "Failed to write to chroot pipe";
return false;
}
// We need to reap the chroot helper process in any event.
pid_t helper_pid = GetHelperPID(env_.get());
// If helper_pid is -1 we wait for any child.
if (HANDLE_EINTR(waitpid(helper_pid, NULL, 0)) < 0) {
PLOG(ERROR) << "Failed to wait for setuid helper to die";
return false;
}
char reply;
if (HANDLE_EINTR(read(ipc_fd, &reply, 1)) != 1) {
PLOG(ERROR) << "Failed to read from chroot pipe";
return false;
}
if (reply != kMsgChrootSuccessful) {
LOG(ERROR) << "Error code reply from chroot helper";
return false;
}
// We now consider ourselves "fully sandboxed" as far as the
// setuid sandbox is concerned.
CHECK(IsFileSystemAccessDenied());
sandboxed_ = true;
return true;
}
bool SetuidSandboxClient::IsSuidSandboxUpToDate() const {
return GetHelperApi(env_.get()) == kSUIDSandboxApiNumber;
}
bool SetuidSandboxClient::IsSuidSandboxChild() const {
return GetIPCDescriptor(env_.get()) >= 0;
}
bool SetuidSandboxClient::IsInNewPIDNamespace() const {
return env_->HasVar(kSandboxPIDNSEnvironmentVarName);
}
bool SetuidSandboxClient::IsInNewNETNamespace() const {
return env_->HasVar(kSandboxNETNSEnvironmentVarName);
}
bool SetuidSandboxClient::IsSandboxed() const {
return sandboxed_;
}
} // namespace sandbox
|