OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/common/sandbox_mac.h" | 5 #include "chrome/common/sandbox_mac.h" |
6 | 6 |
7 #include "base/debug_util.h" | 7 #include "base/debug_util.h" |
8 | 8 |
9 #import <Cocoa/Cocoa.h> | 9 #import <Cocoa/Cocoa.h> |
10 extern "C" { | 10 extern "C" { |
11 #include <sandbox.h> | 11 #include <sandbox.h> |
12 } | 12 } |
13 #include <sys/param.h> | 13 #include <sys/param.h> |
14 | 14 |
15 #include "base/basictypes.h" | 15 #include "base/basictypes.h" |
16 #include "base/command_line.h" | 16 #include "base/command_line.h" |
17 #include "base/file_util.h" | 17 #include "base/file_util.h" |
18 #include "base/hash_tables.h" | |
19 #include "base/mac_util.h" | 18 #include "base/mac_util.h" |
20 #include "base/rand_util_c.h" | 19 #include "base/rand_util_c.h" |
21 #include "base/mac/scoped_cftyperef.h" | 20 #include "base/mac/scoped_cftyperef.h" |
22 #include "base/mac/scoped_nsautorelease_pool.h" | 21 #include "base/mac/scoped_nsautorelease_pool.h" |
23 #include "base/string16.h" | 22 #include "base/string16.h" |
24 #include "base/string_util.h" | 23 #include "base/string_util.h" |
25 #include "base/sys_info.h" | 24 #include "base/sys_info.h" |
26 #include "base/sys_string_conversions.h" | 25 #include "base/sys_string_conversions.h" |
27 #include "base/utf_string_conversions.h" | 26 #include "base/utf_string_conversions.h" |
28 #include "chrome/common/chrome_switches.h" | 27 #include "chrome/common/chrome_switches.h" |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
63 } | 62 } |
64 | 63 |
65 dst->append(append); | 64 dst->append(append); |
66 return true; | 65 return true; |
67 } | 66 } |
68 | 67 |
69 } // namespace | 68 } // namespace |
70 | 69 |
71 namespace sandbox { | 70 namespace sandbox { |
72 | 71 |
73 // A map of variable name -> string to substitute in its place. | |
74 typedef base::hash_map<std::string, sandbox::SandboxSubstring> | |
75 SandboxVariableSubstitions; | |
76 | 72 |
77 // Escape |str_utf8| for use in a plain string variable in a sandbox | 73 // static |
78 // configuraton file. On return |dst| is set to the utf-8 encoded quoted | 74 bool Sandbox::QuotePlainString(const std::string& str_utf8, std::string* dst) { |
79 // output. | |
80 // Returns: true on success, false otherwise. | |
81 bool QuotePlainString(const std::string& str_utf8, std::string* dst) { | |
82 dst->clear(); | 75 dst->clear(); |
83 | 76 |
84 const char* src = str_utf8.c_str(); | 77 const char* src = str_utf8.c_str(); |
85 int32_t length = str_utf8.length(); | 78 int32_t length = str_utf8.length(); |
86 int32_t position = 0; | 79 int32_t position = 0; |
87 while (position < length) { | 80 while (position < length) { |
88 UChar32 c; | 81 UChar32 c; |
89 U8_NEXT(src, position, length, c); // Macro increments |position|. | 82 U8_NEXT(src, position, length, c); // Macro increments |position|. |
90 DCHECK_GE(c, 0); | 83 DCHECK_GE(c, 0); |
91 if (c < 0) | 84 if (c < 0) |
(...skipping 14 matching lines...) Expand all Loading... |
106 } | 99 } |
107 | 100 |
108 // If we got here we know that the character in question is strictly | 101 // If we got here we know that the character in question is strictly |
109 // in the ASCII range so there's no need to do any kind of encoding | 102 // in the ASCII range so there's no need to do any kind of encoding |
110 // conversion. | 103 // conversion. |
111 dst->push_back(static_cast<char>(c)); | 104 dst->push_back(static_cast<char>(c)); |
112 } | 105 } |
113 return true; | 106 return true; |
114 } | 107 } |
115 | 108 |
116 // Escape |str_utf8| for use in a regex literal in a sandbox | 109 // static |
117 // configuraton file. On return |dst| is set to the utf-8 encoded quoted | 110 bool Sandbox::QuoteStringForRegex(const std::string& str_utf8, |
118 // output. | 111 std::string* dst) { |
119 // | |
120 // The implementation of this function is based on empirical testing of the | |
121 // OS X sandbox on 10.5.8 & 10.6.2 which is undocumented and subject to change. | |
122 // | |
123 // Note: If str_utf8 contains any characters < 32 || >125 then the function | |
124 // fails and false is returned. | |
125 // | |
126 // Returns: true on success, false otherwise. | |
127 bool QuoteStringForRegex(const std::string& str_utf8, std::string* dst) { | |
128 // Characters with special meanings in sandbox profile syntax. | 112 // Characters with special meanings in sandbox profile syntax. |
129 const char regex_special_chars[] = { | 113 const char regex_special_chars[] = { |
130 '\\', | 114 '\\', |
131 | 115 |
132 // Metacharacters | 116 // Metacharacters |
133 '^', | 117 '^', |
134 '.', | 118 '.', |
135 '[', | 119 '[', |
136 ']', | 120 ']', |
137 '$', | 121 '$', |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
184 return true; | 168 return true; |
185 } | 169 } |
186 | 170 |
187 // Warm up System APIs that empirically need to be accessed before the Sandbox | 171 // Warm up System APIs that empirically need to be accessed before the Sandbox |
188 // is turned on. | 172 // is turned on. |
189 // This method is layed out in blocks, each one containing a separate function | 173 // This method is layed out in blocks, each one containing a separate function |
190 // that needs to be warmed up. The OS version on which we found the need to | 174 // that needs to be warmed up. The OS version on which we found the need to |
191 // enable the function is also noted. | 175 // enable the function is also noted. |
192 // This function is tested on the following OS versions: | 176 // This function is tested on the following OS versions: |
193 // 10.5.6, 10.6.0 | 177 // 10.5.6, 10.6.0 |
194 void SandboxWarmup() { | 178 |
| 179 // static |
| 180 void Sandbox::SandboxWarmup() { |
195 base::mac::ScopedNSAutoreleasePool scoped_pool; | 181 base::mac::ScopedNSAutoreleasePool scoped_pool; |
196 | 182 |
197 { // CGColorSpaceCreateWithName(), CGBitmapContextCreate() - 10.5.6 | 183 { // CGColorSpaceCreateWithName(), CGBitmapContextCreate() - 10.5.6 |
198 base::mac::ScopedCFTypeRef<CGColorSpaceRef> rgb_colorspace( | 184 base::mac::ScopedCFTypeRef<CGColorSpaceRef> rgb_colorspace( |
199 CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)); | 185 CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)); |
200 | 186 |
201 // Allocate a 1x1 image. | 187 // Allocate a 1x1 image. |
202 char data[4]; | 188 char data[4]; |
203 base::mac::ScopedCFTypeRef<CGContextRef> context( | 189 base::mac::ScopedCFTypeRef<CGContextRef> context( |
204 CGBitmapContextCreate(data, 1, 1, 8, 1 * 4, | 190 CGBitmapContextCreate(data, 1, 1, 8, 1 * 4, |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
240 CGImageSourceCreateWithData((CFDataRef)data, | 226 CGImageSourceCreateWithData((CFDataRef)data, |
241 NULL)); | 227 NULL)); |
242 CGImageSourceGetStatus(img); | 228 CGImageSourceGetStatus(img); |
243 } | 229 } |
244 | 230 |
245 { // Native Client access to /dev/random. | 231 { // Native Client access to /dev/random. |
246 GetUrandomFD(); | 232 GetUrandomFD(); |
247 } | 233 } |
248 } | 234 } |
249 | 235 |
250 // Build the Sandbox command necessary to allow access to a named directory | 236 // static |
251 // indicated by |allowed_dir|. | 237 NSString* Sandbox::BuildAllowDirectoryAccessSandboxString( |
252 // Returns a string containing the sandbox profile commands necessary to allow | |
253 // access to that directory or nil if an error occured. | |
254 | |
255 // The header comment for PostProcessSandboxProfile() explains how variable | |
256 // substition works in sandbox templates. | |
257 // The returned string contains embedded variables. The function fills in | |
258 // |substitutions| to contain the values for these variables. | |
259 NSString* BuildAllowDirectoryAccessSandboxString( | |
260 const FilePath& allowed_dir, | 238 const FilePath& allowed_dir, |
261 SandboxVariableSubstitions* substitutions) { | 239 SandboxVariableSubstitions* substitutions) { |
262 // A whitelist is used to determine which directories can be statted | 240 // A whitelist is used to determine which directories can be statted |
263 // This means that in the case of an /a/b/c/d/ directory, we may be able to | 241 // This means that in the case of an /a/b/c/d/ directory, we may be able to |
264 // stat the leaf directory, but not it's parent. | 242 // stat the leaf directory, but not it's parent. |
265 // The extension code in Chrome calls realpath() which fails if it can't call | 243 // The extension code in Chrome calls realpath() which fails if it can't call |
266 // stat() on one of the parent directories in the path. | 244 // stat() on one of the parent directories in the path. |
267 // The solution to this is to allow statting the parent directories themselves | 245 // The solution to this is to allow statting the parent directories themselves |
268 // but not their contents. We need to add a separate rule for each parent | 246 // but not their contents. We need to add a separate rule for each parent |
269 // directory. | 247 // directory. |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
310 SandboxSubstring::REGEX); | 288 SandboxSubstring::REGEX); |
311 sandbox_command = | 289 sandbox_command = |
312 [sandbox_command | 290 [sandbox_command |
313 stringByAppendingString:@") (allow file-read* file-write*" | 291 stringByAppendingString:@") (allow file-read* file-write*" |
314 " (regex #\"@ALLOWED_DIR@\") )"]; | 292 " (regex #\"@ALLOWED_DIR@\") )"]; |
315 return sandbox_command; | 293 return sandbox_command; |
316 } | 294 } |
317 | 295 |
318 // Load the appropriate template for the given sandbox type. | 296 // Load the appropriate template for the given sandbox type. |
319 // Returns the template as an NSString or nil on error. | 297 // Returns the template as an NSString or nil on error. |
320 NSString* LoadSandboxTemplate(SandboxProcessType sandbox_type) { | 298 NSString* LoadSandboxTemplate(Sandbox::SandboxProcessType sandbox_type) { |
321 // We use a custom sandbox definition file to lock things down as | 299 // We use a custom sandbox definition file to lock things down as |
322 // tightly as possible. | 300 // tightly as possible. |
323 NSString* sandbox_config_filename = nil; | 301 NSString* sandbox_config_filename = nil; |
324 switch (sandbox_type) { | 302 switch (sandbox_type) { |
325 case SANDBOX_TYPE_RENDERER: | 303 case Sandbox::SANDBOX_TYPE_RENDERER: |
326 sandbox_config_filename = @"renderer"; | 304 sandbox_config_filename = @"renderer"; |
327 break; | 305 break; |
328 case SANDBOX_TYPE_WORKER: | 306 case Sandbox::SANDBOX_TYPE_WORKER: |
329 sandbox_config_filename = @"worker"; | 307 sandbox_config_filename = @"worker"; |
330 break; | 308 break; |
331 case SANDBOX_TYPE_UTILITY: | 309 case Sandbox::SANDBOX_TYPE_UTILITY: |
332 sandbox_config_filename = @"utility"; | 310 sandbox_config_filename = @"utility"; |
333 break; | 311 break; |
334 case SANDBOX_TYPE_NACL_LOADER: | 312 case Sandbox::SANDBOX_TYPE_NACL_LOADER: |
335 // The Native Client loader is used for safeguarding the user's | 313 // The Native Client loader is used for safeguarding the user's |
336 // untrusted code within Native Client. | 314 // untrusted code within Native Client. |
337 sandbox_config_filename = @"nacl_loader"; | 315 sandbox_config_filename = @"nacl_loader"; |
338 break; | 316 break; |
339 default: | 317 default: |
340 NOTREACHED(); | 318 NOTREACHED(); |
341 return nil; | 319 return nil; |
342 } | 320 } |
343 | 321 |
344 // Read in the sandbox profile and the common prefix file. | 322 // Read in the sandbox profile and the common prefix file. |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
377 // Retrieve OS X version, output parameters are self explanatory. | 355 // Retrieve OS X version, output parameters are self explanatory. |
378 void GetOSVersion(bool* snow_leopard_or_higher) { | 356 void GetOSVersion(bool* snow_leopard_or_higher) { |
379 int32 major_version, minor_version, bugfix_version; | 357 int32 major_version, minor_version, bugfix_version; |
380 base::SysInfo::OperatingSystemVersionNumbers(&major_version, | 358 base::SysInfo::OperatingSystemVersionNumbers(&major_version, |
381 &minor_version, | 359 &minor_version, |
382 &bugfix_version); | 360 &bugfix_version); |
383 *snow_leopard_or_higher = | 361 *snow_leopard_or_higher = |
384 (major_version > 10 || (major_version == 10 && minor_version >= 6)); | 362 (major_version > 10 || (major_version == 10 && minor_version >= 6)); |
385 } | 363 } |
386 | 364 |
387 // Assemble the final sandbox profile from a template by removing comments | 365 // static |
388 // and substituting variables. | 366 bool Sandbox::PostProcessSandboxProfile( |
389 // | 367 NSString* sandbox_template, |
390 // |sandbox_template| is a string which contains 2 entitites to operate on: | 368 NSArray* comments_to_remove, |
391 // | 369 SandboxVariableSubstitions& substitutions, |
392 // - Comments - The sandbox comment syntax is used to make the OS sandbox | 370 std::string *final_sandbox_profile_str) { |
393 // optionally ignore commands it doesn't support. e.g. | |
394 // ;10.6_ONLY (foo) | |
395 // Where (foo) is some command that is only supported on OS X 10.6. | |
396 // The ;10.6_ONLY comment can then be removed from the template to enable (foo) | |
397 // as appropriate. | |
398 // | |
399 // - Variables - denoted by @variable_name@ . These are defined in the sandbox | |
400 // template in cases where another string needs to be substituted at runtime. | |
401 // e.g. @HOMEDIR_AS_LITERAL@ is substituted at runtime for the user's home | |
402 // directory escaped appropriately for a (literal ...) expression. | |
403 // | |
404 // |comments_to_remove| is a list of NSStrings containing the comments to | |
405 // remove. | |
406 // |substitutions| is a hash of "variable name" -> "string to substitute". | |
407 // Where the replacement string is tagged with information on how it is to be | |
408 // escaped e.g. used as part of a regex string or a literal. | |
409 // | |
410 // On output |final_sandbox_profile_str| contains the final sandbox profile. | |
411 // Returns true on success, false otherwise. | |
412 bool PostProcessSandboxProfile(NSString* sandbox_template, | |
413 NSArray* comments_to_remove, | |
414 SandboxVariableSubstitions& substitutions, | |
415 std::string *final_sandbox_profile_str) { | |
416 NSString* sandbox_data = [[sandbox_template copy] autorelease]; | 371 NSString* sandbox_data = [[sandbox_template copy] autorelease]; |
417 | 372 |
418 // Remove comments, e.g. ;10.6_ONLY . | 373 // Remove comments, e.g. ;10.6_ONLY . |
419 for (NSString* to_remove in comments_to_remove) { | 374 for (NSString* to_remove in comments_to_remove) { |
420 sandbox_data = [sandbox_data stringByReplacingOccurrencesOfString:to_remove | 375 sandbox_data = [sandbox_data stringByReplacingOccurrencesOfString:to_remove |
421 withString:@""]; | 376 withString:@""]; |
422 } | 377 } |
423 | 378 |
424 // Split string on "@" characters. | 379 // Split string on "@" characters. |
425 std::vector<std::string> raw_sandbox_pieces; | 380 std::vector<std::string> raw_sandbox_pieces; |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
468 for (std::vector<std::string>::iterator it = processed_sandbox_pieces.begin(); | 423 for (std::vector<std::string>::iterator it = processed_sandbox_pieces.begin(); |
469 it != processed_sandbox_pieces.end(); | 424 it != processed_sandbox_pieces.end(); |
470 ++it) { | 425 ++it) { |
471 final_sandbox_profile_str->append(*it); | 426 final_sandbox_profile_str->append(*it); |
472 } | 427 } |
473 return true; | 428 return true; |
474 } | 429 } |
475 | 430 |
476 | 431 |
477 // Turns on the OS X sandbox for this process. | 432 // Turns on the OS X sandbox for this process. |
478 bool EnableSandbox(SandboxProcessType sandbox_type, | 433 |
479 const FilePath& allowed_dir) { | 434 // static |
| 435 bool Sandbox::EnableSandbox(SandboxProcessType sandbox_type, |
| 436 const FilePath& allowed_dir) { |
480 // Sanity - currently only SANDBOX_TYPE_UTILITY supports a directory being | 437 // Sanity - currently only SANDBOX_TYPE_UTILITY supports a directory being |
481 // passed in. | 438 // passed in. |
482 if (sandbox_type != SANDBOX_TYPE_UTILITY) { | 439 if (sandbox_type != SANDBOX_TYPE_UTILITY) { |
483 DCHECK(allowed_dir.empty()) | 440 DCHECK(allowed_dir.empty()) |
484 << "Only SANDBOX_TYPE_UTILITY allows a custom directory parameter."; | 441 << "Only SANDBOX_TYPE_UTILITY allows a custom directory parameter."; |
485 } | 442 } |
486 | 443 |
487 NSString* sandbox_data = LoadSandboxTemplate(sandbox_type); | 444 NSString* sandbox_data = LoadSandboxTemplate(sandbox_type); |
488 if (!sandbox_data) { | 445 if (!sandbox_data) { |
489 return false; | 446 return false; |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
563 int error = sandbox_init(final_sandbox_profile_str.c_str(), 0, &error_buff); | 520 int error = sandbox_init(final_sandbox_profile_str.c_str(), 0, &error_buff); |
564 bool success = (error == 0 && error_buff == NULL); | 521 bool success = (error == 0 && error_buff == NULL); |
565 LOG_IF(FATAL, !success) << "Failed to initialize sandbox: " | 522 LOG_IF(FATAL, !success) << "Failed to initialize sandbox: " |
566 << error | 523 << error |
567 << " " | 524 << " " |
568 << error_buff; | 525 << error_buff; |
569 sandbox_free_error(error_buff); | 526 sandbox_free_error(error_buff); |
570 return success; | 527 return success; |
571 } | 528 } |
572 | 529 |
573 void GetCanonicalSandboxPath(FilePath* path) { | 530 // static |
| 531 void Sandbox::GetCanonicalSandboxPath(FilePath* path) { |
574 int fd = HANDLE_EINTR(open(path->value().c_str(), O_RDONLY)); | 532 int fd = HANDLE_EINTR(open(path->value().c_str(), O_RDONLY)); |
575 if (fd < 0) { | 533 if (fd < 0) { |
576 PLOG(FATAL) << "GetCanonicalSandboxPath() failed for: " | 534 PLOG(FATAL) << "GetCanonicalSandboxPath() failed for: " |
577 << path->value(); | 535 << path->value(); |
578 return; | 536 return; |
579 } | 537 } |
580 file_util::ScopedFD file_closer(&fd); | 538 file_util::ScopedFD file_closer(&fd); |
581 | 539 |
582 FilePath::CharType canonical_path[MAXPATHLEN]; | 540 FilePath::CharType canonical_path[MAXPATHLEN]; |
583 if (HANDLE_EINTR(fcntl(fd, F_GETPATH, canonical_path)) != 0) { | 541 if (HANDLE_EINTR(fcntl(fd, F_GETPATH, canonical_path)) != 0) { |
584 PLOG(FATAL) << "GetCanonicalSandboxPath() failed for: " | 542 PLOG(FATAL) << "GetCanonicalSandboxPath() failed for: " |
585 << path->value(); | 543 << path->value(); |
586 return; | 544 return; |
587 } | 545 } |
588 | 546 |
589 *path = FilePath(canonical_path); | 547 *path = FilePath(canonical_path); |
590 } | 548 } |
591 | 549 |
592 } // namespace sandbox | 550 } // namespace sandbox |
OLD | NEW |