| 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 |