| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include <asm/unistd.h> | |
| 6 #include <dlfcn.h> | |
| 7 #include <errno.h> | |
| 8 #include <fcntl.h> | |
| 9 #include <linux/net.h> | |
| 10 #include <signal.h> | |
| 11 #include <string.h> | |
| 12 #include <sys/ioctl.h> | |
| 13 #include <sys/mman.h> | |
| 14 #include <sys/prctl.h> | |
| 15 #include <sys/socket.h> | |
| 16 #include <sys/stat.h> | |
| 17 #include <sys/types.h> | |
| 18 #include <ucontext.h> | |
| 19 #include <unistd.h> | |
| 20 | |
| 21 #include <vector> | |
| 22 | |
| 23 #include "base/basictypes.h" | |
| 24 #include "base/command_line.h" | |
| 25 #include "base/logging.h" | |
| 26 #include "build/build_config.h" | |
| 27 #include "content/public/common/content_switches.h" | |
| 28 | |
| 29 // These are the only architectures supported for now. | |
| 30 #if defined(__i386__) || defined(__x86_64__) || \ | |
| 31 (defined(__arm__) && (defined(__thumb__) || defined(__ARM_EABI__))) | |
| 32 #define SECCOMP_BPF_SANDBOX | |
| 33 #endif | |
| 34 | |
| 35 #if defined(SECCOMP_BPF_SANDBOX) | |
| 36 #include "base/posix/eintr_wrapper.h" | |
| 37 #include "content/common/sandbox_linux/bpf_cros_arm_gpu_policy_linux.h" | |
| 38 #include "content/common/sandbox_linux/bpf_gpu_policy_linux.h" | |
| 39 #include "content/common/sandbox_linux/sandbox_bpf_base_policy_linux.h" | |
| 40 #include "content/common/sandbox_linux/sandbox_linux.h" | |
| 41 #include "content/common/sandbox_linux/sandbox_seccomp_bpf_linux.h" | |
| 42 #include "sandbox/linux/seccomp-bpf-helpers/baseline_policy.h" | |
| 43 #include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h" | |
| 44 #include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h" | |
| 45 #include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h" | |
| 46 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" | |
| 47 #include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h" | |
| 48 #include "sandbox/linux/services/linux_syscalls.h" | |
| 49 | |
| 50 using sandbox::BaselinePolicy; | |
| 51 using sandbox::ErrorCode; | |
| 52 using sandbox::SandboxBPF; | |
| 53 using sandbox::SyscallSets; | |
| 54 using sandbox::arch_seccomp_data; | |
| 55 | |
| 56 namespace content { | |
| 57 | |
| 58 namespace { | |
| 59 | |
| 60 void StartSandboxWithPolicy(sandbox::SandboxBPFPolicy* policy); | |
| 61 | |
| 62 inline bool IsChromeOS() { | |
| 63 #if defined(OS_CHROMEOS) | |
| 64 return true; | |
| 65 #else | |
| 66 return false; | |
| 67 #endif | |
| 68 } | |
| 69 | |
| 70 inline bool IsArchitectureArm() { | |
| 71 #if defined(__arm__) | |
| 72 return true; | |
| 73 #else | |
| 74 return false; | |
| 75 #endif | |
| 76 } | |
| 77 | |
| 78 inline bool IsUsingToolKitGtk() { | |
| 79 #if defined(TOOLKIT_GTK) | |
| 80 return true; | |
| 81 #else | |
| 82 return false; | |
| 83 #endif | |
| 84 } | |
| 85 | |
| 86 // Policy for renderer and worker processes. | |
| 87 // TODO(jln): move to renderer/ | |
| 88 | |
| 89 class RendererOrWorkerProcessPolicy : public SandboxBPFBasePolicy { | |
| 90 public: | |
| 91 RendererOrWorkerProcessPolicy() {} | |
| 92 virtual ~RendererOrWorkerProcessPolicy() {} | |
| 93 | |
| 94 virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler, | |
| 95 int system_call_number) const OVERRIDE; | |
| 96 | |
| 97 private: | |
| 98 DISALLOW_COPY_AND_ASSIGN(RendererOrWorkerProcessPolicy); | |
| 99 }; | |
| 100 | |
| 101 ErrorCode RendererOrWorkerProcessPolicy::EvaluateSyscall(SandboxBPF* sandbox, | |
| 102 int sysno) const { | |
| 103 switch (sysno) { | |
| 104 case __NR_clone: | |
| 105 return sandbox::RestrictCloneToThreadsAndEPERMFork(sandbox); | |
| 106 case __NR_ioctl: | |
| 107 return sandbox::RestrictIoctl(sandbox); | |
| 108 case __NR_prctl: | |
| 109 return sandbox::RestrictPrctl(sandbox); | |
| 110 // Allow the system calls below. | |
| 111 case __NR_fdatasync: | |
| 112 case __NR_fsync: | |
| 113 case __NR_getpriority: | |
| 114 #if defined(__i386__) || defined(__x86_64__) | |
| 115 case __NR_getrlimit: | |
| 116 #endif | |
| 117 #if defined(__i386__) || defined(__arm__) | |
| 118 case __NR_ugetrlimit: | |
| 119 #endif | |
| 120 case __NR_mremap: // See crbug.com/149834. | |
| 121 case __NR_pread64: | |
| 122 case __NR_pwrite64: | |
| 123 case __NR_sched_getaffinity: | |
| 124 case __NR_sched_get_priority_max: | |
| 125 case __NR_sched_get_priority_min: | |
| 126 case __NR_sched_getparam: | |
| 127 case __NR_sched_getscheduler: | |
| 128 case __NR_sched_setscheduler: | |
| 129 case __NR_setpriority: | |
| 130 case __NR_sysinfo: | |
| 131 case __NR_times: | |
| 132 case __NR_uname: | |
| 133 return ErrorCode(ErrorCode::ERR_ALLOWED); | |
| 134 case __NR_prlimit64: | |
| 135 return ErrorCode(EPERM); // See crbug.com/160157. | |
| 136 default: | |
| 137 if (IsUsingToolKitGtk()) { | |
| 138 #if defined(__x86_64__) || defined(__arm__) | |
| 139 if (SyscallSets::IsSystemVSharedMemory(sysno)) | |
| 140 return ErrorCode(ErrorCode::ERR_ALLOWED); | |
| 141 #endif | |
| 142 #if defined(__i386__) | |
| 143 if (SyscallSets::IsSystemVIpc(sysno)) | |
| 144 return ErrorCode(ErrorCode::ERR_ALLOWED); | |
| 145 #endif | |
| 146 } | |
| 147 | |
| 148 // Default on the content baseline policy. | |
| 149 return SandboxBPFBasePolicy::EvaluateSyscall(sandbox, sysno); | |
| 150 } | |
| 151 } | |
| 152 | |
| 153 // Policy for PPAPI plugins. | |
| 154 // TODO(jln): move to ppapi_plugin/. | |
| 155 class FlashProcessPolicy : public SandboxBPFBasePolicy { | |
| 156 public: | |
| 157 FlashProcessPolicy() {} | |
| 158 virtual ~FlashProcessPolicy() {} | |
| 159 | |
| 160 virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler, | |
| 161 int system_call_number) const OVERRIDE; | |
| 162 | |
| 163 private: | |
| 164 DISALLOW_COPY_AND_ASSIGN(FlashProcessPolicy); | |
| 165 }; | |
| 166 | |
| 167 ErrorCode FlashProcessPolicy::EvaluateSyscall(SandboxBPF* sandbox, | |
| 168 int sysno) const { | |
| 169 switch (sysno) { | |
| 170 case __NR_clone: | |
| 171 return sandbox::RestrictCloneToThreadsAndEPERMFork(sandbox); | |
| 172 case __NR_pread64: | |
| 173 case __NR_pwrite64: | |
| 174 case __NR_sched_get_priority_max: | |
| 175 case __NR_sched_get_priority_min: | |
| 176 case __NR_sched_getaffinity: | |
| 177 case __NR_sched_getparam: | |
| 178 case __NR_sched_getscheduler: | |
| 179 case __NR_sched_setscheduler: | |
| 180 case __NR_times: | |
| 181 return ErrorCode(ErrorCode::ERR_ALLOWED); | |
| 182 case __NR_ioctl: | |
| 183 return ErrorCode(ENOTTY); // Flash Access. | |
| 184 default: | |
| 185 if (IsUsingToolKitGtk()) { | |
| 186 #if defined(__x86_64__) || defined(__arm__) | |
| 187 if (SyscallSets::IsSystemVSharedMemory(sysno)) | |
| 188 return ErrorCode(ErrorCode::ERR_ALLOWED); | |
| 189 #endif | |
| 190 #if defined(__i386__) | |
| 191 if (SyscallSets::IsSystemVIpc(sysno)) | |
| 192 return ErrorCode(ErrorCode::ERR_ALLOWED); | |
| 193 #endif | |
| 194 } | |
| 195 | |
| 196 // Default on the baseline policy. | |
| 197 return SandboxBPFBasePolicy::EvaluateSyscall(sandbox, sysno); | |
| 198 } | |
| 199 } | |
| 200 | |
| 201 class BlacklistDebugAndNumaPolicy : public SandboxBPFBasePolicy { | |
| 202 public: | |
| 203 BlacklistDebugAndNumaPolicy() {} | |
| 204 virtual ~BlacklistDebugAndNumaPolicy() {} | |
| 205 | |
| 206 virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler, | |
| 207 int system_call_number) const OVERRIDE; | |
| 208 | |
| 209 private: | |
| 210 DISALLOW_COPY_AND_ASSIGN(BlacklistDebugAndNumaPolicy); | |
| 211 }; | |
| 212 | |
| 213 ErrorCode BlacklistDebugAndNumaPolicy::EvaluateSyscall(SandboxBPF* sandbox, | |
| 214 int sysno) const { | |
| 215 if (!SandboxBPF::IsValidSyscallNumber(sysno)) { | |
| 216 // TODO(jln) we should not have to do that in a trivial policy. | |
| 217 return ErrorCode(ENOSYS); | |
| 218 } | |
| 219 if (SyscallSets::IsDebug(sysno) || SyscallSets::IsNuma(sysno)) | |
| 220 return sandbox->Trap(sandbox::CrashSIGSYS_Handler, NULL); | |
| 221 | |
| 222 return ErrorCode(ErrorCode::ERR_ALLOWED); | |
| 223 } | |
| 224 | |
| 225 class AllowAllPolicy : public SandboxBPFBasePolicy { | |
| 226 public: | |
| 227 AllowAllPolicy() {} | |
| 228 virtual ~AllowAllPolicy() {} | |
| 229 | |
| 230 virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler, | |
| 231 int system_call_number) const OVERRIDE; | |
| 232 | |
| 233 private: | |
| 234 DISALLOW_COPY_AND_ASSIGN(AllowAllPolicy); | |
| 235 }; | |
| 236 | |
| 237 // Allow all syscalls. | |
| 238 // This will still deny x32 or IA32 calls in 64 bits mode or | |
| 239 // 64 bits system calls in compatibility mode. | |
| 240 ErrorCode AllowAllPolicy::EvaluateSyscall(SandboxBPF*, int sysno) const { | |
| 241 if (!SandboxBPF::IsValidSyscallNumber(sysno)) { | |
| 242 // TODO(jln) we should not have to do that in a trivial policy. | |
| 243 return ErrorCode(ENOSYS); | |
| 244 } else { | |
| 245 return ErrorCode(ErrorCode::ERR_ALLOWED); | |
| 246 } | |
| 247 } | |
| 248 | |
| 249 // If a BPF policy is engaged for |process_type|, run a few sanity checks. | |
| 250 void RunSandboxSanityChecks(const std::string& process_type) { | |
| 251 if (process_type == switches::kRendererProcess || | |
| 252 process_type == switches::kWorkerProcess || | |
| 253 process_type == switches::kGpuProcess || | |
| 254 process_type == switches::kPpapiPluginProcess) { | |
| 255 int syscall_ret; | |
| 256 errno = 0; | |
| 257 | |
| 258 // Without the sandbox, this would EBADF. | |
| 259 syscall_ret = fchmod(-1, 07777); | |
| 260 CHECK_EQ(-1, syscall_ret); | |
| 261 CHECK_EQ(EPERM, errno); | |
| 262 | |
| 263 // Run most of the sanity checks only in DEBUG mode to avoid a perf. | |
| 264 // impact. | |
| 265 #if !defined(NDEBUG) | |
| 266 // open() must be restricted. | |
| 267 syscall_ret = open("/etc/passwd", O_RDONLY); | |
| 268 CHECK_EQ(-1, syscall_ret); | |
| 269 CHECK_EQ(SandboxBPFBasePolicy::GetFSDeniedErrno(), errno); | |
| 270 | |
| 271 // We should never allow the creation of netlink sockets. | |
| 272 syscall_ret = socket(AF_NETLINK, SOCK_DGRAM, 0); | |
| 273 CHECK_EQ(-1, syscall_ret); | |
| 274 CHECK_EQ(EPERM, errno); | |
| 275 #endif // !defined(NDEBUG) | |
| 276 } | |
| 277 } | |
| 278 | |
| 279 | |
| 280 // This function takes ownership of |policy|. | |
| 281 void StartSandboxWithPolicy(sandbox::SandboxBPFPolicy* policy) { | |
| 282 // Starting the sandbox is a one-way operation. The kernel doesn't allow | |
| 283 // us to unload a sandbox policy after it has been started. Nonetheless, | |
| 284 // in order to make the use of the "Sandbox" object easier, we allow for | |
| 285 // the object to be destroyed after the sandbox has been started. Note that | |
| 286 // doing so does not stop the sandbox. | |
| 287 SandboxBPF sandbox; | |
| 288 sandbox.SetSandboxPolicy(policy); | |
| 289 sandbox.StartSandbox(); | |
| 290 } | |
| 291 | |
| 292 // nacl_helper needs to be tiny and includes only part of content/ | |
| 293 // in its dependencies. Make sure to not link things that are not needed. | |
| 294 #if !defined(IN_NACL_HELPER) | |
| 295 scoped_ptr<SandboxBPFBasePolicy> GetGpuProcessSandbox() { | |
| 296 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); | |
| 297 bool allow_sysv_shm = false; | |
| 298 if (command_line.HasSwitch(switches::kGpuSandboxAllowSysVShm)) { | |
| 299 DCHECK(IsArchitectureArm()); | |
| 300 allow_sysv_shm = true; | |
| 301 } | |
| 302 | |
| 303 if (IsChromeOS() && IsArchitectureArm()) { | |
| 304 return scoped_ptr<SandboxBPFBasePolicy>( | |
| 305 new CrosArmGpuProcessPolicy(allow_sysv_shm)); | |
| 306 } else { | |
| 307 return scoped_ptr<SandboxBPFBasePolicy>(new GpuProcessPolicy); | |
| 308 } | |
| 309 } | |
| 310 | |
| 311 // Initialize the seccomp-bpf sandbox. | |
| 312 bool StartBPFSandbox(const CommandLine& command_line, | |
| 313 const std::string& process_type) { | |
| 314 scoped_ptr<SandboxBPFBasePolicy> policy; | |
| 315 | |
| 316 if (process_type == switches::kGpuProcess) { | |
| 317 policy.reset(GetGpuProcessSandbox().release()); | |
| 318 } else if (process_type == switches::kRendererProcess || | |
| 319 process_type == switches::kWorkerProcess) { | |
| 320 policy.reset(new RendererOrWorkerProcessPolicy); | |
| 321 } else if (process_type == switches::kPpapiPluginProcess) { | |
| 322 policy.reset(new FlashProcessPolicy); | |
| 323 } else if (process_type == switches::kUtilityProcess) { | |
| 324 policy.reset(new BlacklistDebugAndNumaPolicy); | |
| 325 } else { | |
| 326 NOTREACHED(); | |
| 327 policy.reset(new AllowAllPolicy); | |
| 328 } | |
| 329 | |
| 330 CHECK(policy->PreSandboxHook()); | |
| 331 StartSandboxWithPolicy(policy.release()); | |
| 332 | |
| 333 RunSandboxSanityChecks(process_type); | |
| 334 return true; | |
| 335 } | |
| 336 #else // defined(IN_NACL_HELPER) | |
| 337 bool StartBPFSandbox(const CommandLine& command_line, | |
| 338 const std::string& process_type) { | |
| 339 NOTREACHED(); | |
| 340 // Avoid -Wunused-function with no-op code. | |
| 341 ignore_result(IsChromeOS); | |
| 342 ignore_result(IsArchitectureArm); | |
| 343 ignore_result(RunSandboxSanityChecks); | |
| 344 return false; | |
| 345 } | |
| 346 #endif // !defined(IN_NACL_HELPER) | |
| 347 | |
| 348 } // namespace | |
| 349 | |
| 350 #endif // SECCOMP_BPF_SANDBOX | |
| 351 | |
| 352 // Is seccomp BPF globally enabled? | |
| 353 bool SandboxSeccompBPF::IsSeccompBPFDesired() { | |
| 354 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); | |
| 355 if (!command_line.HasSwitch(switches::kNoSandbox) && | |
| 356 !command_line.HasSwitch(switches::kDisableSeccompFilterSandbox)) { | |
| 357 return true; | |
| 358 } else { | |
| 359 return false; | |
| 360 } | |
| 361 } | |
| 362 | |
| 363 bool SandboxSeccompBPF::ShouldEnableSeccompBPF( | |
| 364 const std::string& process_type) { | |
| 365 #if defined(SECCOMP_BPF_SANDBOX) | |
| 366 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); | |
| 367 if (process_type == switches::kGpuProcess) | |
| 368 return !command_line.HasSwitch(switches::kDisableGpuSandbox); | |
| 369 | |
| 370 return true; | |
| 371 #endif // SECCOMP_BPF_SANDBOX | |
| 372 return false; | |
| 373 } | |
| 374 | |
| 375 bool SandboxSeccompBPF::SupportsSandbox() { | |
| 376 #if defined(SECCOMP_BPF_SANDBOX) | |
| 377 // TODO(jln): pass the saved proc_fd_ from the LinuxSandbox singleton | |
| 378 // here. | |
| 379 SandboxBPF::SandboxStatus bpf_sandbox_status = | |
| 380 SandboxBPF::SupportsSeccompSandbox(-1); | |
| 381 // Kernel support is what we are interested in here. Other status | |
| 382 // such as STATUS_UNAVAILABLE (has threads) still indicate kernel support. | |
| 383 // We make this a negative check, since if there is a bug, we would rather | |
| 384 // "fail closed" (expect a sandbox to be available and try to start it). | |
| 385 if (bpf_sandbox_status != SandboxBPF::STATUS_UNSUPPORTED) { | |
| 386 return true; | |
| 387 } | |
| 388 #endif | |
| 389 return false; | |
| 390 } | |
| 391 | |
| 392 bool SandboxSeccompBPF::StartSandbox(const std::string& process_type) { | |
| 393 #if defined(SECCOMP_BPF_SANDBOX) | |
| 394 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); | |
| 395 | |
| 396 if (IsSeccompBPFDesired() && // Global switches policy. | |
| 397 ShouldEnableSeccompBPF(process_type) && // Process-specific policy. | |
| 398 SupportsSandbox()) { | |
| 399 // If the kernel supports the sandbox, and if the command line says we | |
| 400 // should enable it, enable it or die. | |
| 401 bool started_sandbox = StartBPFSandbox(command_line, process_type); | |
| 402 CHECK(started_sandbox); | |
| 403 return true; | |
| 404 } | |
| 405 #endif | |
| 406 return false; | |
| 407 } | |
| 408 | |
| 409 bool SandboxSeccompBPF::StartSandboxWithExternalPolicy( | |
| 410 scoped_ptr<sandbox::SandboxBPFPolicy> policy) { | |
| 411 #if defined(SECCOMP_BPF_SANDBOX) | |
| 412 if (IsSeccompBPFDesired() && SupportsSandbox()) { | |
| 413 CHECK(policy); | |
| 414 StartSandboxWithPolicy(policy.release()); | |
| 415 return true; | |
| 416 } | |
| 417 #endif // defined(SECCOMP_BPF_SANDBOX) | |
| 418 return false; | |
| 419 } | |
| 420 | |
| 421 scoped_ptr<sandbox::SandboxBPFPolicy> | |
| 422 SandboxSeccompBPF::GetBaselinePolicy() { | |
| 423 #if defined(SECCOMP_BPF_SANDBOX) | |
| 424 return scoped_ptr<sandbox::SandboxBPFPolicy>(new BaselinePolicy); | |
| 425 #else | |
| 426 return scoped_ptr<sandbox::SandboxBPFPolicy>(); | |
| 427 #endif // defined(SECCOMP_BPF_SANDBOX) | |
| 428 } | |
| 429 | |
| 430 } // namespace content | |
| OLD | NEW |