| // Copyright (c) 2012 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 "content/common/sandbox_mac.h" |
| |
| #import <Cocoa/Cocoa.h> |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <CoreFoundation/CFTimeZone.h> |
| #include <signal.h> |
| #include <sys/param.h> |
| |
| #include <algorithm> |
| #include <iterator> |
| |
| #include "base/command_line.h" |
| #include "base/compiler_specific.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_file.h" |
| #include "base/mac/bundle_locations.h" |
| #include "base/mac/foundation_util.h" |
| #include "base/mac/mac_util.h" |
| #include "base/mac/scoped_cftyperef.h" |
| #include "base/mac/scoped_nsautorelease_pool.h" |
| #include "base/mac/scoped_nsobject.h" |
| #include "base/macros.h" |
| #include "base/rand_util.h" |
| #include "base/strings/string16.h" |
| #include "base/strings/string_piece.h" |
| #include "base/strings/string_split.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/sys_string_conversions.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/sys_info.h" |
| #include "content/public/common/content_client.h" |
| #include "content/public/common/content_switches.h" |
| #include "media/gpu/vt_video_decode_accelerator_mac.h" |
| #include "sandbox/mac/sandbox_compiler.h" |
| #include "services/service_manager/sandbox/mac/common.sb.h" |
| #include "services/service_manager/sandbox/mac/gpu.sb.h" |
| #include "services/service_manager/sandbox/mac/nacl_loader.sb.h" |
| #include "services/service_manager/sandbox/mac/ppapi.sb.h" |
| #include "services/service_manager/sandbox/mac/renderer.sb.h" |
| #include "services/service_manager/sandbox/mac/utility.sb.h" |
| #include "services/service_manager/sandbox/sandbox_type.h" |
| #include "third_party/icu/source/common/unicode/uchar.h" |
| #include "ui/base/layout.h" |
| #include "ui/gl/init/gl_factory.h" |
| |
| namespace content { |
| namespace { |
| |
| // Is the sandbox currently active. |
| bool gSandboxIsActive = false; |
| |
| struct SandboxTypeToResourceIDMapping { |
| service_manager::SandboxType sandbox_type; |
| const char* seatbelt_policy_string; |
| }; |
| |
| // Mapping from sandbox process types to resource IDs containing the sandbox |
| // profile for all process types known to content. |
| // TODO(tsepez): Implement profile for SANDBOX_TYPE_NETWORK. |
| SandboxTypeToResourceIDMapping kDefaultSandboxTypeToResourceIDMapping[] = { |
| {service_manager::SANDBOX_TYPE_NO_SANDBOX, nullptr}, |
| {service_manager::SANDBOX_TYPE_RENDERER, |
| service_manager::kSeatbeltPolicyString_renderer}, |
| {service_manager::SANDBOX_TYPE_UTILITY, |
| service_manager::kSeatbeltPolicyString_utility}, |
| {service_manager::SANDBOX_TYPE_GPU, |
| service_manager::kSeatbeltPolicyString_gpu}, |
| {service_manager::SANDBOX_TYPE_PPAPI, |
| service_manager::kSeatbeltPolicyString_ppapi}, |
| {service_manager::SANDBOX_TYPE_NETWORK, nullptr}, |
| {service_manager::SANDBOX_TYPE_CDM, |
| service_manager::kSeatbeltPolicyString_ppapi}, |
| {service_manager::SANDBOX_TYPE_NACL_LOADER, |
| service_manager::kSeatbeltPolicyString_nacl_loader}, |
| {service_manager::SANDBOX_TYPE_PDF_COMPOSITOR, |
| service_manager::kSeatbeltPolicyString_ppapi}, |
| }; |
| |
| static_assert(arraysize(kDefaultSandboxTypeToResourceIDMapping) == |
| size_t(service_manager::SANDBOX_TYPE_AFTER_LAST_TYPE), |
| "sandbox type to resource id mapping incorrect"); |
| |
| } // namespace |
| |
| // Static variable declarations. |
| const char* Sandbox::kSandboxBrowserPID = "BROWSER_PID"; |
| const char* Sandbox::kSandboxBundlePath = "BUNDLE_PATH"; |
| const char* Sandbox::kSandboxChromeBundleId = "BUNDLE_ID"; |
| const char* Sandbox::kSandboxComponentPath = "COMPONENT_PATH"; |
| const char* Sandbox::kSandboxDisableDenialLogging = |
| "DISABLE_SANDBOX_DENIAL_LOGGING"; |
| const char* Sandbox::kSandboxEnableLogging = "ENABLE_LOGGING"; |
| const char* Sandbox::kSandboxHomedirAsLiteral = "USER_HOMEDIR_AS_LITERAL"; |
| const char* Sandbox::kSandboxLoggingPathAsLiteral = "LOG_FILE_PATH"; |
| const char* Sandbox::kSandboxOSVersion = "OS_VERSION"; |
| const char* Sandbox::kSandboxPermittedDir = "PERMITTED_DIR"; |
| const char* Sandbox::kSandboxElCapOrLater = "ELCAP_OR_LATER"; |
| const char* Sandbox::kSandboxMacOS1013 = "MACOS_1013"; |
| |
| // Warm up System APIs that empirically need to be accessed before the Sandbox |
| // is turned on. |
| // This method is layed out in blocks, each one containing a separate function |
| // that needs to be warmed up. The OS version on which we found the need to |
| // enable the function is also noted. |
| // This function is tested on the following OS versions: |
| // 10.5.6, 10.6.0 |
| |
| // static |
| void Sandbox::SandboxWarmup(service_manager::SandboxType sandbox_type) { |
| base::mac::ScopedNSAutoreleasePool scoped_pool; |
| |
| { // CGColorSpaceCreateWithName(), CGBitmapContextCreate() - 10.5.6 |
| base::ScopedCFTypeRef<CGColorSpaceRef> rgb_colorspace( |
| CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)); |
| |
| // Allocate a 1x1 image. |
| char data[4]; |
| base::ScopedCFTypeRef<CGContextRef> context(CGBitmapContextCreate( |
| data, |
| 1, |
| 1, |
| 8, |
| 1 * 4, |
| rgb_colorspace, |
| kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host)); |
| |
| // Load in the color profiles we'll need (as a side effect). |
| ignore_result(base::mac::GetSRGBColorSpace()); |
| ignore_result(base::mac::GetSystemColorSpace()); |
| |
| // CGColorSpaceCreateSystemDefaultCMYK - 10.6 |
| base::ScopedCFTypeRef<CGColorSpaceRef> cmyk_colorspace( |
| CGColorSpaceCreateWithName(kCGColorSpaceGenericCMYK)); |
| } |
| |
| { // localtime() - 10.5.6 |
| time_t tv = {0}; |
| localtime(&tv); |
| } |
| |
| { // Gestalt() tries to read /System/Library/CoreServices/SystemVersion.plist |
| // on 10.5.6 |
| int32_t tmp; |
| base::SysInfo::OperatingSystemVersionNumbers(&tmp, &tmp, &tmp); |
| } |
| |
| { // CGImageSourceGetStatus() - 10.6 |
| // Create a png with just enough data to get everything warmed up... |
| char png_header[] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}; |
| NSData* data = [NSData dataWithBytes:png_header |
| length:arraysize(png_header)]; |
| base::ScopedCFTypeRef<CGImageSourceRef> img( |
| CGImageSourceCreateWithData((CFDataRef)data, NULL)); |
| CGImageSourceGetStatus(img); |
| } |
| |
| { |
| // Allow access to /dev/urandom. |
| base::GetUrandomFD(); |
| } |
| |
| { // IOSurfaceLookup() - 10.7 |
| // Needed by zero-copy texture update framework - crbug.com/323338 |
| base::ScopedCFTypeRef<IOSurfaceRef> io_surface(IOSurfaceLookup(0)); |
| } |
| |
| // Process-type dependent warm-up. |
| if (sandbox_type == service_manager::SANDBOX_TYPE_UTILITY) { |
| // CFTimeZoneCopyZone() tries to read /etc and /private/etc/localtime - 10.8 |
| // Needed by Media Galleries API Picasa - crbug.com/151701 |
| CFTimeZoneCopySystem(); |
| } |
| |
| if (sandbox_type == service_manager::SANDBOX_TYPE_GPU) { |
| // Preload either the desktop GL or the osmesa so, depending on the |
| // --use-gl flag. |
| gl::init::InitializeGLOneOff(); |
| |
| // Preload VideoToolbox. |
| media::InitializeVideoToolbox(); |
| } |
| |
| if (sandbox_type == service_manager::SANDBOX_TYPE_PPAPI) { |
| // Preload AppKit color spaces used for Flash/ppapi. http://crbug.com/348304 |
| NSColor* color = [NSColor controlTextColor]; |
| [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; |
| } |
| } |
| |
| // Load the appropriate template for the given sandbox type. |
| // Returns the template as a string or an empty string on error. |
| std::string LoadSandboxTemplate(service_manager::SandboxType sandbox_type) { |
| // We use a custom sandbox definition to lock things down as tightly as |
| // possible. |
| auto* it = std::find_if( |
| std::begin(kDefaultSandboxTypeToResourceIDMapping), |
| std::end(kDefaultSandboxTypeToResourceIDMapping), |
| [sandbox_type](const SandboxTypeToResourceIDMapping& element) { |
| return element.sandbox_type == sandbox_type; |
| }); |
| |
| CHECK(it != std::end(kDefaultSandboxTypeToResourceIDMapping)) |
| << "Unknown sandbox type " << sandbox_type; |
| base::StringPiece sandbox_definition = it->seatbelt_policy_string; |
| |
| // Prefix sandbox_data with common_sandbox_prefix_data. |
| std::string sandbox_profile = service_manager::kSeatbeltPolicyString_common; |
| sandbox_definition.AppendToString(&sandbox_profile); |
| return sandbox_profile; |
| } |
| |
| // Turns on the OS X sandbox for this process. |
| |
| // static |
| bool Sandbox::EnableSandbox(service_manager::SandboxType sandbox_type, |
| const base::FilePath& allowed_dir) { |
| // Sanity - currently only some sandboxes support a directory being |
| // passed in. |
| if (sandbox_type != service_manager::SANDBOX_TYPE_UTILITY) { |
| DCHECK(allowed_dir.empty()) |
| << "Only SANDBOX_TYPE_UTILITY allows a custom directory parameter."; |
| } |
| |
| std::string sandbox_data = LoadSandboxTemplate(sandbox_type); |
| if (sandbox_data.empty()) |
| return false; |
| |
| sandbox::SandboxCompiler compiler(sandbox_data); |
| |
| if (!allowed_dir.empty()) { |
| // Add the sandbox parameters necessary to access the given directory. |
| base::FilePath allowed_dir_canonical = GetCanonicalSandboxPath(allowed_dir); |
| if (!compiler.InsertStringParam(kSandboxPermittedDir, |
| allowed_dir_canonical.value())) |
| return false; |
| } |
| |
| // Enable verbose logging if enabled on the command line. (See common.sb |
| // for details). |
| const base::CommandLine* command_line = |
| base::CommandLine::ForCurrentProcess(); |
| bool enable_logging = |
| command_line->HasSwitch(switches::kEnableSandboxLogging);; |
| if (!compiler.InsertBooleanParam(kSandboxEnableLogging, enable_logging)) |
| return false; |
| |
| // Without this, the sandbox will print a message to the system log every |
| // time it denies a request. This floods the console with useless spew. |
| if (!compiler.InsertBooleanParam(kSandboxDisableDenialLogging, |
| !enable_logging)) |
| return false; |
| |
| // Splice the path of the user's home directory into the sandbox profile |
| // (see renderer.sb for details). |
| std::string home_dir = [NSHomeDirectory() fileSystemRepresentation]; |
| |
| base::FilePath home_dir_canonical = |
| GetCanonicalSandboxPath(base::FilePath(home_dir)); |
| |
| if (!compiler.InsertStringParam(kSandboxHomedirAsLiteral, |
| home_dir_canonical.value())) |
| return false; |
| |
| bool elcap_or_later = base::mac::IsAtLeastOS10_11(); |
| if (!compiler.InsertBooleanParam(kSandboxElCapOrLater, elcap_or_later)) |
| return false; |
| |
| bool macos_1013 = base::mac::IsOS10_13(); |
| if (!compiler.InsertBooleanParam(kSandboxMacOS1013, macos_1013)) |
| return false; |
| |
| // Initialize sandbox. |
| std::string error_str; |
| bool success = compiler.CompileAndApplyProfile(&error_str); |
| DLOG_IF(FATAL, !success) << "Failed to initialize sandbox: " << error_str; |
| gSandboxIsActive = success; |
| return success; |
| } |
| |
| // static |
| bool Sandbox::SandboxIsCurrentlyActive() { |
| return gSandboxIsActive; |
| } |
| |
| // static |
| base::FilePath Sandbox::GetCanonicalSandboxPath(const base::FilePath& path) { |
| base::ScopedFD fd(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY))); |
| if (!fd.is_valid()) { |
| DPLOG(FATAL) << "GetCanonicalSandboxPath() failed for: " |
| << path.value(); |
| return path; |
| } |
| |
| base::FilePath::CharType canonical_path[MAXPATHLEN]; |
| if (HANDLE_EINTR(fcntl(fd.get(), F_GETPATH, canonical_path)) != 0) { |
| DPLOG(FATAL) << "GetCanonicalSandboxPath() failed for: " |
| << path.value(); |
| return path; |
| } |
| |
| return base::FilePath(canonical_path); |
| } |
| |
| } // namespace content |