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 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
|
// 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.
#ifndef SANDBOX_POLICY_LINUX_SANDBOX_LINUX_H_
#define SANDBOX_POLICY_LINUX_SANDBOX_LINUX_H_
#include <memory>
#include <string>
#include <vector>
#include "base/check_op.h"
#include "base/memory/raw_ptr.h"
#include "base/posix/global_descriptors.h"
#include "base/sanitizer_buildflags.h"
#include "sandbox/linux/syscall_broker/broker_command.h"
#include "sandbox/linux/syscall_broker/broker_file_permission.h"
#include "sandbox/policy/export.h"
#include "sandbox/policy/linux/sandbox_seccomp_bpf_linux.h"
#if BUILDFLAG(USING_SANITIZER)
#include <sanitizer/common_interface_defs.h>
#endif
namespace base {
template <typename T>
struct DefaultSingletonTraits;
class Thread;
} // namespace base
namespace sandbox {
namespace mojom {
enum class Sandbox;
} // namespace mojom
namespace syscall_broker {
class BrokerProcess;
} // namespace syscall_broker
class SetuidSandboxClient;
} // namespace sandbox
namespace sandbox {
namespace policy {
// A singleton class to represent and change our sandboxing state for the
// three main Linux sandboxes.
// The sandboxing model allows using two layers of sandboxing. The first layer
// can be implemented either with unprivileged namespaces or with the setuid
// sandbox. This class provides a way to engage the namespace sandbox, but does
// not deal with the legacy setuid sandbox directly.
// The second layer is mainly based on seccomp-bpf and is engaged with
// InitializeSandbox(). InitializeSandbox() is also responsible for "sealing"
// the first layer of sandboxing. That is, InitializeSandbox must always be
// called to have any meaningful sandboxing at all.
class SANDBOX_POLICY_EXPORT SandboxLinux {
public:
// This is a list of sandbox IPC methods which the renderer may send to the
// sandbox host. See
// https://chromium.googlesource.com/chromium/src/+/main/docs/linux/sandbox_ipc.md
// This isn't the full list, values < 32 are reserved for methods called from
// Skia, and values < 64 are reserved for libc_interceptor.cc.
enum LinuxSandboxIPCMethods {
DEPRECATED_METHOD_GET_FALLBACK_FONT_FOR_CHAR = 64,
DEPRECATED_METHOD_GET_CHILD_WITH_INODE,
DEPRECATED_METHOD_GET_STYLE_FOR_STRIKE,
METHOD_MAKE_SHARED_MEMORY_SEGMENT,
DEPRECATED_METHOD_MATCH_WITH_FALLBACK,
};
// These form a bitmask which describes the conditions of the Linux sandbox.
// Note: this doesn't strictly give you the current status, it states
// what will be enabled when the relevant processes are initialized.
enum Status {
// SUID sandbox active.
kSUID = 1 << 0,
// Sandbox is using a new PID namespace.
kPIDNS = 1 << 1,
// Sandbox is using a new network namespace.
kNetNS = 1 << 2,
// seccomp-bpf sandbox active.
kSeccompBPF = 1 << 3,
// The Yama LSM module is present and enforcing.
kYama = 1 << 4,
// seccomp-bpf sandbox is active and the kernel supports TSYNC.
kSeccompTSYNC = 1 << 5,
// User namespace sandbox active.
kUserNS = 1 << 6,
// A flag that denotes an invalid sandbox status.
kInvalid = 1 << 31,
};
// SandboxLinux Options are a superset of SandboxSecompBPF Options.
struct Options : public SandboxSeccompBPF::Options {
// When running with a zygote, the namespace sandbox will have already
// been engaged prior to initializing SandboxLinux itself, and need not
// be done so again. Set to true to indicate that there isn't a zygote
// for this process and the step is to be performed here explicitly.
bool engage_namespace_sandbox = false;
// Allow starting the sandbox with multiple threads already running. This
// will enable TSYNC for seccomp-BPF, which syncs the seccomp-BPF policy
// across all running threads.
bool allow_threads_during_sandbox_init = false;
// Enables the CHECK for open directories. The open directory check is only
// useful for the chroot jail (from the semantic layer of the sandbox), and
// can safely be disabled if we are only enabling the seccomp-BPF layer.
bool check_for_open_directories = true;
};
// Callers can provide this hook to run code right before the policy
// is passed to the BPF compiler and the sandbox is engaged. If
// pre_sandbox_hook() returns true, the sandbox will be engaged
// afterwards, otherwise the process is terminated.
using PreSandboxHook = base::OnceCallback<bool(Options)>;
// Get our singleton instance.
static SandboxLinux* GetInstance();
SandboxLinux(const SandboxLinux&) = delete;
SandboxLinux& operator=(const SandboxLinux&) = delete;
// Do some initialization that can only be done before any of the sandboxes
// are enabled. If using the setuid sandbox, this should be called manually
// before the setuid sandbox is engaged.
// Security: When this runs, it is imperative that either InitializeSandbox()
// runs as well or that all file descriptors returned in
// GetFileDescriptorsToClose() get closed.
// Otherwise file descriptors that bypass the security of the setuid sandbox
// would be kept open. One must be particularly careful if a process performs
// a fork().
void PreinitializeSandbox();
// Check that the current process is the init process of a new PID
// namespace and then proceed to drop access to the file system by using
// a new unprivileged namespace. This is a layer-1 sandbox.
// In order for this sandbox to be effective, it must be "sealed" by calling
// InitializeSandbox().
// Terminates the process in case the sandboxing operations cannot complete
// successfully.
void EngageNamespaceSandbox(bool from_zygote);
// Performs the same actions as EngageNamespaceSandbox, but is allowed to
// to fail. This is useful when sandboxed non-renderer processes could
// benefit from extra sandboxing but is not strictly required on systems that
// don't support unprivileged user namespaces.
// Zygote should use EngageNamespaceSandbox instead.
bool EngageNamespaceSandboxIfPossible();
// Return a list of file descriptors to close if PreinitializeSandbox() ran
// but InitializeSandbox() won't. Avoid using.
// TODO(jln): get rid of this hack.
std::vector<int> GetFileDescriptorsToClose();
// Seal an eventual layer-1 sandbox and initialize the layer-2 sandbox with
// an adequate policy depending on the process type and command line
// arguments.
// Currently the layer-2 sandbox is composed of seccomp-bpf and address space
// limitations.
// This function should only be called without any thread running.
bool InitializeSandbox(sandbox::mojom::Sandbox sandbox_type,
PreSandboxHook hook,
const Options& options);
// Stop |thread| in a way that can be trusted by the sandbox.
void StopThread(base::Thread* thread);
// Returns the status of the renderer, worker and ppapi sandbox. Can only
// be queried after going through PreinitializeSandbox(). This is a bitmask
// and uses the constants defined in "enum Status" above. Since the
// status needs to be provided before the sandboxes are actually started,
// this returns what will actually happen once InitializeSandbox()
// is called from inside these processes.
int GetStatus();
// Returns true if the current process is single-threaded or if the number
// of threads cannot be determined.
bool IsSingleThreaded() const;
// Returns true if we started Seccomp BPF.
bool seccomp_bpf_started() const;
// Simple accessor for our instance of the setuid sandbox. Will never return
// NULL.
// There is no StartSetuidSandbox(), the SetuidSandboxClient instance should
// be used directly.
SetuidSandboxClient* setuid_sandbox_client() const;
// Check the policy and eventually start the seccomp-bpf sandbox. Fine to be
// called with threads, as long as
// |options.allow_threads_during_sandbox_init| is true and the kernel
// supports seccomp's TSYNC feature. If TSYNC is not available we treat
// multiple threads as a fatal error.
bool StartSeccompBPF(sandbox::mojom::Sandbox sandbox_type,
PreSandboxHook hook,
const Options& options);
// Limit the address space of the current process (and its children) to make
// some vulnerabilities harder to exploit. Writes the errno due to setrlimit
// (including 0 if no error) into |error|.
bool LimitAddressSpace(int* error);
// Returns a file descriptor to proc. The file descriptor is no longer valid
// after the sandbox has been sealed.
int proc_fd() const {
DCHECK_NE(-1, proc_fd_);
return proc_fd_;
}
#if BUILDFLAG(USING_SANITIZER)
__sanitizer_sandbox_arguments* sanitizer_args() const {
return sanitizer_args_.get();
}
#endif
// A BrokerProcess is a helper that is started before the sandbox is engaged,
// typically from a pre-sandbox hook, that will serve requests to access
// files over an IPC channel. The client of this runs from a SIGSYS handler
// triggered by the seccomp-bpf sandbox.
//
// |policy| is the policy being run by the client, and is used to derive the
// equivalent broker-side policy.
void StartBrokerProcess(
const syscall_broker::BrokerCommandSet& allowed_command_set,
std::vector<syscall_broker::BrokerFilePermission> permissions,
const Options& options);
// Returns true if the broker should handle a particular syscall indicated by
// |sysno|. This will typically return true for system calls that take
// filepaths as arguments.
bool ShouldBrokerHandleSyscall(int sysno) const;
// Returns an expression that indicates the syscall in question should be
// handled transparently by the broker process. This is useful for file
// syscalls that take pathnames, so we can enforce pathname whitelisting.
// Only usable if StartBrokerProcess() was already called.
bpf_dsl::ResultExpr HandleViaBroker(int sysno) const;
// Reports Landlock status through UMA metrics.
static void ReportLandlockStatus();
private:
friend struct base::DefaultSingletonTraits<SandboxLinux>;
SandboxLinux();
~SandboxLinux();
// We must have been pre_initialized_ before using these.
bool seccomp_bpf_supported() const;
bool seccomp_bpf_with_tsync_supported() const;
// Returns true if it can be determined that the current process has open
// directories that are not managed by the SandboxLinux class. This would
// be a vulnerability as it would allow to bypass the setuid sandbox.
bool HasOpenDirectories() const;
// The last part of the initialization is to make sure any temporary "hole"
// in the sandbox is closed. For now, this consists of closing proc_fd_.
void SealSandbox();
// GetStatus() makes promises as to how the sandbox will behave. This
// checks that no promises have been broken.
void CheckForBrokenPromises(sandbox::mojom::Sandbox sandbox_type);
// Stop |thread| and make sure it does not appear in /proc/self/tasks/
// anymore.
void StopThreadAndEnsureNotCounted(base::Thread* thread) const;
// Engages the namespace sandbox as described for EngageNamespaceSandbox.
// Returns false if it fails to transition to a new user namespace, but
// after transitioning to a new user namespace we don't allow this function
// to fail.
bool EngageNamespaceSandboxInternal(bool from_zygote);
// A file descriptor to /proc. It's dangerous to have it around as it could
// allow for sandbox bypasses. It needs to be closed before we consider
// ourselves sandboxed.
int proc_fd_;
bool seccomp_bpf_started_;
// The value returned by GetStatus(). Gets computed once and then cached.
int sandbox_status_flags_;
// Did PreinitializeSandbox() run?
bool pre_initialized_;
bool seccomp_bpf_supported_; // Accurate if pre_initialized_.
bool seccomp_bpf_with_tsync_supported_; // Accurate if pre_initialized_.
bool yama_is_enforcing_; // Accurate if pre_initialized_.
bool initialize_sandbox_ran_; // InitializeSandbox() was called.
std::unique_ptr<SetuidSandboxClient> setuid_sandbox_client_;
#if BUILDFLAG(USING_SANITIZER)
std::unique_ptr<__sanitizer_sandbox_arguments> sanitizer_args_;
#endif
raw_ptr<syscall_broker::BrokerProcess> broker_process_; // Leaked as global.
};
} // namespace policy
} // namespace sandbox
#endif // SANDBOX_POLICY_LINUX_SANDBOX_LINUX_H_
|