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 "services/service_manager/sandbox/mac/sandbox_mac.h"
6
7#import <Cocoa/Cocoa.h>
8#include <stddef.h>
9#include <stdint.h>
10
11#include <CoreFoundation/CFTimeZone.h>
12#include <signal.h>
13#include <sys/param.h>
14
15#include <algorithm>
16#include <iterator>
17#include <map>
18#include <string>
19
20#include "base/command_line.h"
21#include "base/compiler_specific.h"
22#include "base/files/file_util.h"
23#include "base/files/scoped_file.h"
24#include "base/mac/bundle_locations.h"
25#include "base/mac/foundation_util.h"
26#include "base/mac/mac_util.h"
27#include "base/mac/mach_port_rendezvous.h"
28#include "base/mac/scoped_cftyperef.h"
29#include "base/mac/scoped_nsobject.h"
30#include "base/rand_util.h"
31#include "base/stl_util.h"
32#include "base/strings/string16.h"
33#include "base/strings/string_piece.h"
34#include "base/strings/string_split.h"
35#include "base/strings/string_util.h"
36#include "base/strings/stringprintf.h"
37#include "base/strings/sys_string_conversions.h"
38#include "base/strings/utf_string_conversions.h"
39#include "base/system/sys_info.h"
40#include "sandbox/mac/sandbox_compiler.h"
41#include "services/service_manager/sandbox/mac/audio.sb.h"
42#include "services/service_manager/sandbox/mac/cdm.sb.h"
43#include "services/service_manager/sandbox/mac/common.sb.h"
44#include "services/service_manager/sandbox/mac/gpu.sb.h"
45#include "services/service_manager/sandbox/mac/gpu_v2.sb.h"
46#include "services/service_manager/sandbox/mac/nacl_loader.sb.h"
47#include "services/service_manager/sandbox/mac/network.sb.h"
48#include "services/service_manager/sandbox/mac/ppapi.sb.h"
49#include "services/service_manager/sandbox/mac/print_compositor.sb.h"
50#include "services/service_manager/sandbox/mac/renderer.sb.h"
51#include "services/service_manager/sandbox/mac/utility.sb.h"
52#include "services/service_manager/sandbox/sandbox_type.h"
53#include "services/service_manager/sandbox/switches.h"
54
55namespace service_manager {
56
57// Static variable declarations.
58const char* SandboxMac::kSandboxBrowserPID = "BROWSER_PID";
59const char* SandboxMac::kSandboxBundlePath = "BUNDLE_PATH";
60const char* SandboxMac::kSandboxChromeBundleId = "BUNDLE_ID";
61const char* SandboxMac::kSandboxComponentPath = "COMPONENT_PATH";
62#if defined(TOOLKIT_QT)
63const char* SandboxMac::kSandboxQtPrefixPath = "QT_PREFIX_PATH";
64#endif
65const char* SandboxMac::kSandboxDisableDenialLogging =
66    "DISABLE_SANDBOX_DENIAL_LOGGING";
67const char* SandboxMac::kSandboxEnableLogging = "ENABLE_LOGGING";
68const char* SandboxMac::kSandboxHomedirAsLiteral = "USER_HOMEDIR_AS_LITERAL";
69const char* SandboxMac::kSandboxLoggingPathAsLiteral = "LOG_FILE_PATH";
70const char* SandboxMac::kSandboxOSVersion = "OS_VERSION";
71const char* SandboxMac::kSandboxElCapOrLater = "ELCAP_OR_LATER";
72const char* SandboxMac::kSandboxMacOS1013 = "MACOS_1013";
73const char* SandboxMac::kSandboxFieldTrialSeverName = "FIELD_TRIAL_SERVER_NAME";
74const char* SandboxMac::kSandboxBundleVersionPath = "BUNDLE_VERSION_PATH";
75
76// Warm up System APIs that empirically need to be accessed before the Sandbox
77// is turned on.
78// This method is layed out in blocks, each one containing a separate function
79// that needs to be warmed up. The OS version on which we found the need to
80// enable the function is also noted.
81// This function is tested on the following OS versions:
82//     10.5.6, 10.6.0
83
84// static
85void SandboxMac::Warmup(SandboxType sandbox_type) {
86  DCHECK_EQ(sandbox_type, SandboxType::kGpu);
87
88  @autoreleasepool {
89    {  // CGColorSpaceCreateWithName(), CGBitmapContextCreate() - 10.5.6
90      base::ScopedCFTypeRef<CGColorSpaceRef> rgb_colorspace(
91          CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB));
92
93      // Allocate a 1x1 image.
94      char data[4];
95      base::ScopedCFTypeRef<CGContextRef> context(CGBitmapContextCreate(
96          data, 1, 1, 8, 1 * 4, rgb_colorspace,
97          kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host));
98
99      // Load in the color profiles we'll need (as a side effect).
100      ignore_result(base::mac::GetSRGBColorSpace());
101      ignore_result(base::mac::GetSystemColorSpace());
102
103      // CGColorSpaceCreateSystemDefaultCMYK - 10.6
104      base::ScopedCFTypeRef<CGColorSpaceRef> cmyk_colorspace(
105          CGColorSpaceCreateWithName(kCGColorSpaceGenericCMYK));
106    }
107
108    {  // localtime() - 10.5.6
109      time_t tv = {0};
110      localtime(&tv);
111    }
112
113    {  // Gestalt() tries to read
114       // /System/Library/CoreServices/SystemVersion.plist
115      // on 10.5.6
116      int32_t tmp;
117      base::SysInfo::OperatingSystemVersionNumbers(&tmp, &tmp, &tmp);
118    }
119
120    {  // CGImageSourceGetStatus() - 10.6
121       // Create a png with just enough data to get everything warmed up...
122      char png_header[] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
123      NSData* data = [NSData dataWithBytes:png_header
124                                    length:base::size(png_header)];
125      base::ScopedCFTypeRef<CGImageSourceRef> img(
126          CGImageSourceCreateWithData((CFDataRef)data, NULL));
127      CGImageSourceGetStatus(img);
128    }
129
130    {
131      // Allow access to /dev/urandom.
132      base::GetUrandomFD();
133    }
134
135    {  // IOSurfaceLookup() - 10.7
136      // Needed by zero-copy texture update framework - crbug.com/323338
137      base::ScopedCFTypeRef<IOSurfaceRef> io_surface(IOSurfaceLookup(0));
138    }
139  }
140}
141
142// Load the appropriate template for the given sandbox type.
143// Returns the template as a string or an empty string on error.
144std::string LoadSandboxTemplate(SandboxType sandbox_type) {
145  DCHECK_EQ(sandbox_type, SandboxType::kGpu);
146  return kSeatbeltPolicyString_gpu;
147}
148
149// Turns on the OS X sandbox for this process.
150
151// static
152bool SandboxMac::Enable(SandboxType sandbox_type) {
153  DCHECK_EQ(sandbox_type, SandboxType::kGpu);
154
155  std::string sandbox_data = LoadSandboxTemplate(sandbox_type);
156  if (sandbox_data.empty())
157    return false;
158
159  sandbox::SandboxCompiler compiler(sandbox_data);
160
161  // Enable verbose logging if enabled on the command line. (See common.sb
162  // for details).
163  const base::CommandLine* command_line =
164      base::CommandLine::ForCurrentProcess();
165  bool enable_logging =
166      command_line->HasSwitch(switches::kEnableSandboxLogging);
167  if (!compiler.InsertBooleanParam(kSandboxEnableLogging, enable_logging))
168    return false;
169
170  // Without this, the sandbox will print a message to the system log every
171  // time it denies a request.  This floods the console with useless spew.
172  if (!compiler.InsertBooleanParam(kSandboxDisableDenialLogging,
173                                   !enable_logging))
174    return false;
175
176  // Splice the path of the user's home directory into the sandbox profile
177  // (see renderer.sb for details).
178  std::string home_dir = [NSHomeDirectory() fileSystemRepresentation];
179  base::FilePath home_dir_canonical =
180      GetCanonicalPath(base::FilePath(home_dir));
181
182  if (!compiler.InsertStringParam(kSandboxHomedirAsLiteral,
183                                  home_dir_canonical.value())) {
184    return false;
185  }
186
187  if (!compiler.InsertStringParam(
188          kSandboxFieldTrialSeverName,
189          base::MachPortRendezvousClient::GetBootstrapName())) {
190    return false;
191  }
192
193  bool elcap_or_later = base::mac::IsAtLeastOS10_11();
194  if (!compiler.InsertBooleanParam(kSandboxElCapOrLater, elcap_or_later))
195    return false;
196
197  bool macos_1013 = base::mac::IsOS10_13();
198  if (!compiler.InsertBooleanParam(kSandboxMacOS1013, macos_1013))
199    return false;
200
201  if (sandbox_type == service_manager::SandboxType::kGpu) {
202    base::FilePath bundle_path =
203        SandboxMac::GetCanonicalPath(base::mac::FrameworkBundlePath());
204    if (!compiler.InsertStringParam(kSandboxBundleVersionPath,
205                                    bundle_path.value()))
206      return false;
207  }
208
209  // Initialize sandbox.
210  std::string error_str;
211  bool success = compiler.CompileAndApplyProfile(&error_str);
212  DLOG_IF(FATAL, !success) << "Failed to initialize sandbox: " << error_str;
213  return success;
214}
215
216// static
217base::FilePath SandboxMac::GetCanonicalPath(const base::FilePath& path) {
218  base::ScopedFD fd(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY)));
219  if (!fd.is_valid()) {
220    DPLOG(ERROR) << "GetCanonicalSandboxPath() failed for: " << path.value();
221    return path;
222  }
223
224  base::FilePath::CharType canonical_path[MAXPATHLEN];
225  if (HANDLE_EINTR(fcntl(fd.get(), F_GETPATH, canonical_path)) != 0) {
226    DPLOG(ERROR) << "GetCanonicalSandboxPath() failed for: " << path.value();
227    return path;
228  }
229
230  return base::FilePath(canonical_path);
231}
232
233// static
234std::string SandboxMac::GetSandboxProfile(SandboxType sandbox_type) {
235  std::string profile =
236      std::string(service_manager::kSeatbeltPolicyString_common);
237
238  switch (sandbox_type) {
239    case service_manager::SandboxType::kAudio:
240      profile += service_manager::kSeatbeltPolicyString_audio;
241      break;
242    case service_manager::SandboxType::kCdm:
243      profile += service_manager::kSeatbeltPolicyString_cdm;
244      break;
245    case service_manager::SandboxType::kGpu:
246      profile += service_manager::kSeatbeltPolicyString_gpu_v2;
247      break;
248    case service_manager::SandboxType::kNaClLoader:
249      profile += service_manager::kSeatbeltPolicyString_nacl_loader;
250      break;
251    case service_manager::SandboxType::kNetwork:
252      profile += service_manager::kSeatbeltPolicyString_network;
253      break;
254    case service_manager::SandboxType::kPpapi:
255      profile += service_manager::kSeatbeltPolicyString_ppapi;
256      break;
257    case service_manager::SandboxType::kPrintCompositor:
258      profile += service_manager::kSeatbeltPolicyString_print_compositor;
259      break;
260    case service_manager::SandboxType::kUtility:
261      profile += service_manager::kSeatbeltPolicyString_utility;
262      break;
263    case service_manager::SandboxType::kRenderer:
264      profile += service_manager::kSeatbeltPolicyString_renderer;
265      break;
266    case service_manager::SandboxType::kNoSandbox:
267    case service_manager::SandboxType::kInvalid:
268    case service_manager::SandboxType::kSoda:
269      CHECK(false);
270      break;
271  }
272  return profile;
273}
274
275}  // namespace service_manager
276