Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(28)

Side by Side Diff: content/common/sandbox_mac.mm

Issue 1186233004: Refactor OS X sandbox processing and audit sandbox files (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Refactor OS X sandbox processing and audit sandbox files Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 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 "content/common/sandbox_mac.h" 5 #include "content/common/sandbox_mac.h"
6 6
7 #import <Cocoa/Cocoa.h> 7 #import <Cocoa/Cocoa.h>
8 8
9 #include <CoreFoundation/CFTimeZone.h> 9 #include <CoreFoundation/CFTimeZone.h>
10 extern "C" { 10 extern "C" {
(...skipping 24 matching lines...) Expand all
35 #include "content/common/gpu/media/vt_video_decode_accelerator.h" 35 #include "content/common/gpu/media/vt_video_decode_accelerator.h"
36 #include "content/grit/content_resources.h" 36 #include "content/grit/content_resources.h"
37 #include "content/public/common/content_client.h" 37 #include "content/public/common/content_client.h"
38 #include "content/public/common/content_switches.h" 38 #include "content/public/common/content_switches.h"
39 #include "third_party/icu/source/common/unicode/uchar.h" 39 #include "third_party/icu/source/common/unicode/uchar.h"
40 #include "ui/base/layout.h" 40 #include "ui/base/layout.h"
41 #include "ui/gl/gl_surface.h" 41 #include "ui/gl/gl_surface.h"
42 42
43 extern "C" { 43 extern "C" {
44 void CGSSetDenyWindowServerConnections(bool); 44 void CGSSetDenyWindowServerConnections(bool);
45 void CGSShutdownServerConnections(); 45 void CGSShutdownServerConnections();
Robert Sesek 2015/06/16 23:52:40 nit: blank line after
Greg K 2015/06/18 20:45:18 Done.
46 void* sandbox_create_params();
47 void sandbox_set_param(void* params, const char* key, const char* value);
Robert Sesek 2015/06/16 23:52:40 This returns int, per the disassembly I provided.
Greg K 2015/06/18 20:45:18 Done.
48 void* sandbox_compile_string(const char* profile_str,
49 void* params,
50 char** error);
51 int sandbox_apply(void* profile);
52 void sandbox_free_params(void* params);
53 void sandbox_free_profile(void* profile);
46 }; 54 };
47 55
48 namespace content { 56 namespace content {
49 namespace { 57 namespace {
50 58
51 // Is the sandbox currently active. 59 // Is the sandbox currently active.
52 bool gSandboxIsActive = false; 60 bool gSandboxIsActive = false;
53 61
54 struct SandboxTypeToResourceIDMapping { 62 struct SandboxTypeToResourceIDMapping {
55 SandboxType sandbox_type; 63 SandboxType sandbox_type;
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
109 // in a central place. 117 // in a central place.
110 NOINLINE void FatalStringQuoteException(const std::string& str) { 118 NOINLINE void FatalStringQuoteException(const std::string& str) {
111 // Copy bad string to the stack so it's recorded in the crash dump. 119 // Copy bad string to the stack so it's recorded in the crash dump.
112 char bad_string[256] = {0}; 120 char bad_string[256] = {0};
113 base::strlcpy(bad_string, str.c_str(), arraysize(bad_string)); 121 base::strlcpy(bad_string, str.c_str(), arraysize(bad_string));
114 DLOG(FATAL) << "String quoting failed " << bad_string; 122 DLOG(FATAL) << "String quoting failed " << bad_string;
115 } 123 }
116 124
117 } // namespace 125 } // namespace
118 126
119 // static 127 SandboxCompiler::SandboxCompiler(const std::string& profile_str)
120 NSString* Sandbox::AllowMetadataForPath(const base::FilePath& allowed_path) { 128 : strings_(),
121 // Collect a list of all parent directories. 129 params_(nullptr),
122 base::FilePath last_path = allowed_path; 130 profile_(nullptr),
123 std::vector<base::FilePath> subpaths; 131 profile_str_(profile_str) {
124 for (base::FilePath path = allowed_path; 132 }
125 path.value() != last_path.value(); 133
126 path = path.DirName()) { 134 SandboxCompiler::~SandboxCompiler() {
127 subpaths.push_back(path); 135 if (params_)
128 last_path = path; 136 sandbox_free_params(params_);
137 if (profile_)
138 sandbox_free_profile(profile_);
139 }
140
141 bool SandboxCompiler::Init() {
142 params_ = sandbox_create_params();
143 return params_ != nullptr;
144 }
145
146 void SandboxCompiler::InsertBooleanParam(const std::string& key, bool value) {
147 strings_.push_back(key);
148 sandbox_set_param(params_, strings_.back().c_str(), value ? "TRUE" : "FALSE");
149 }
150
151 void SandboxCompiler::InsertStringParam(const std::string& key,
152 const std::string& value) {
153 strings_.push_back(key);
154 const char* key_str = strings_.back().c_str();
155 strings_.push_back(value);
156 const char* value_str = strings_.back().c_str();
157
158 sandbox_set_param(params_, key_str, value_str);
159 }
160
161 int SandboxCompiler::CompileAndApplyProfile(std::string* error) {
Robert Sesek 2015/06/16 23:52:40 This should probably return bool.
Greg K 2015/06/18 20:45:18 Done.
162 char* error_internal = nullptr;
163 profile_ =
164 sandbox_compile_string(profile_str_.c_str(), params_, &error_internal);
165 if (!profile_) {
166 error->assign(error_internal);
167 sandbox_free_error(error_internal);
168 return 1;
129 } 169 }
170 // Even if profile_ is created, since we have no guarantee about when
171 // error_internal is allocated, be paranoid about freeing the memory
172 if (error_internal)
173 sandbox_free_error(error_internal);
130 174
131 // Iterate through all parents and allow stat() on them explicitly. 175 int result = sandbox_apply(profile_);
132 NSString* sandbox_command = @"(allow file-read-metadata "; 176 return result;
133 for (std::vector<base::FilePath>::reverse_iterator i = subpaths.rbegin();
134 i != subpaths.rend();
135 ++i) {
136 std::string subdir_escaped;
137 if (!QuotePlainString(i->value(), &subdir_escaped)) {
138 FatalStringQuoteException(i->value());
139 return nil;
140 }
141
142 NSString* subdir_escaped_ns =
143 base::SysUTF8ToNSString(subdir_escaped.c_str());
144 sandbox_command =
145 [sandbox_command stringByAppendingFormat:@"(literal \"%@\")",
146 subdir_escaped_ns];
147 }
148
149 return [sandbox_command stringByAppendingString:@")"];
150 } 177 }
151 178
152 // static 179 // static
153 bool Sandbox::QuotePlainString(const std::string& src_utf8, std::string* dst) { 180 bool Sandbox::QuotePlainString(const std::string& src_utf8, std::string* dst) {
154 dst->clear(); 181 dst->clear();
155 182
156 const char* src = src_utf8.c_str(); 183 const char* src = src_utf8.c_str();
157 int32_t length = src_utf8.length(); 184 int32_t length = src_utf8.length();
158 int32_t position = 0; 185 int32_t position = 0;
159 while (position < length) { 186 while (position < length) {
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after
342 // Shutting down the connection requires connecting to WindowServer, 369 // Shutting down the connection requires connecting to WindowServer,
343 // so do this before actually engaging the sandbox. This is only done on 370 // so do this before actually engaging the sandbox. This is only done on
344 // 10.8 and higher because doing it on earlier OSes causes layout tests to 371 // 10.8 and higher because doing it on earlier OSes causes layout tests to
345 // fail <http://crbug.com/397642#c48>. This may cause two log messages to 372 // fail <http://crbug.com/397642#c48>. This may cause two log messages to
346 // be printed to the system logger on certain OS versions. 373 // be printed to the system logger on certain OS versions.
347 CGSSetDenyWindowServerConnections(true); 374 CGSSetDenyWindowServerConnections(true);
348 CGSShutdownServerConnections(); 375 CGSShutdownServerConnections();
349 } 376 }
350 } 377 }
351 378
352 // static
353 NSString* Sandbox::BuildAllowDirectoryAccessSandboxString(
354 const base::FilePath& allowed_dir,
355 SandboxVariableSubstitions* substitutions) {
356 // A whitelist is used to determine which directories can be statted
357 // This means that in the case of an /a/b/c/d/ directory, we may be able to
358 // stat the leaf directory, but not its parent.
359 // The extension code in Chrome calls realpath() which fails if it can't call
360 // stat() on one of the parent directories in the path.
361 // The solution to this is to allow statting the parent directories themselves
362 // but not their contents. We need to add a separate rule for each parent
363 // directory.
364
365 // The sandbox only understands "real" paths. This resolving step is
366 // needed so the caller doesn't need to worry about things like /var
367 // being a link to /private/var (like in the paths CreateNewTempDirectory()
368 // returns).
369 base::FilePath allowed_dir_canonical = GetCanonicalSandboxPath(allowed_dir);
370
371 NSString* sandbox_command = AllowMetadataForPath(allowed_dir_canonical);
372 sandbox_command = [sandbox_command
373 substringToIndex:[sandbox_command length] - 1]; // strip trailing ')'
374
375 // Finally append the leaf directory. Unlike its parents (for which only
376 // stat() should be allowed), the leaf directory needs full access.
377 (*substitutions)["ALLOWED_DIR"] =
378 SandboxSubstring(allowed_dir_canonical.value(),
379 SandboxSubstring::REGEX);
380 sandbox_command =
381 [sandbox_command
382 stringByAppendingString:@") (allow file-read* file-write*"
383 " (regex #\"@ALLOWED_DIR@\") )"];
384 return sandbox_command;
385 }
386
387 // Load the appropriate template for the given sandbox type. 379 // Load the appropriate template for the given sandbox type.
388 // Returns the template as an NSString or nil on error. 380 // Returns the template as an NSString or nil on error.
389 NSString* LoadSandboxTemplate(int sandbox_type) { 381 NSString* LoadSandboxTemplate(int sandbox_type) {
390 // We use a custom sandbox definition to lock things down as tightly as 382 // We use a custom sandbox definition to lock things down as tightly as
391 // possible. 383 // possible.
392 int sandbox_profile_resource_id = -1; 384 int sandbox_profile_resource_id = -1;
393 385
394 // Find resource id for sandbox profile to use for the specific sandbox type. 386 // Find resource id for sandbox profile to use for the specific sandbox type.
395 for (size_t i = 0; 387 for (size_t i = 0;
396 i < arraysize(kDefaultSandboxTypeToResourceIDMapping); 388 i < arraysize(kDefaultSandboxTypeToResourceIDMapping);
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
434 426
435 base::scoped_nsobject<NSString> sandbox_data( 427 base::scoped_nsobject<NSString> sandbox_data(
436 [[NSString alloc] initWithBytes:sandbox_definition.data() 428 [[NSString alloc] initWithBytes:sandbox_definition.data()
437 length:sandbox_definition.length() 429 length:sandbox_definition.length()
438 encoding:NSUTF8StringEncoding]); 430 encoding:NSUTF8StringEncoding]);
439 431
440 // Prefix sandbox_data with common_sandbox_prefix_data. 432 // Prefix sandbox_data with common_sandbox_prefix_data.
441 return [common_sandbox_prefix_data stringByAppendingString:sandbox_data]; 433 return [common_sandbox_prefix_data stringByAppendingString:sandbox_data];
442 } 434 }
443 435
444 // static
445 bool Sandbox::PostProcessSandboxProfile(
446 NSString* sandbox_template,
447 NSArray* comments_to_remove,
448 SandboxVariableSubstitions& substitutions,
449 std::string *final_sandbox_profile_str) {
450 NSString* sandbox_data = [[sandbox_template copy] autorelease];
451
452 // Remove comments, e.g. ;10.7_OR_ABOVE .
453 for (NSString* to_remove in comments_to_remove) {
454 sandbox_data = [sandbox_data stringByReplacingOccurrencesOfString:to_remove
455 withString:@""];
456 }
457
458 // Split string on "@" characters.
459 std::vector<std::string> raw_sandbox_pieces;
460 if (Tokenize([sandbox_data UTF8String], "@", &raw_sandbox_pieces) == 0) {
461 DLOG(FATAL) << "Bad Sandbox profile, should contain at least one token ("
462 << [sandbox_data UTF8String]
463 << ")";
464 return false;
465 }
466
467 // Iterate over string pieces and substitute variables, escaping as necessary.
468 size_t output_string_length = 0;
469 std::vector<std::string> processed_sandbox_pieces(raw_sandbox_pieces.size());
470 for (std::vector<std::string>::iterator it = raw_sandbox_pieces.begin();
471 it != raw_sandbox_pieces.end();
472 ++it) {
473 std::string new_piece;
474 SandboxVariableSubstitions::iterator replacement_it =
475 substitutions.find(*it);
476 if (replacement_it == substitutions.end()) {
477 new_piece = *it;
478 } else {
479 // Found something to substitute.
480 SandboxSubstring& replacement = replacement_it->second;
481 switch (replacement.type()) {
482 case SandboxSubstring::PLAIN:
483 new_piece = replacement.value();
484 break;
485
486 case SandboxSubstring::LITERAL:
487 if (!QuotePlainString(replacement.value(), &new_piece))
488 FatalStringQuoteException(replacement.value());
489 break;
490
491 case SandboxSubstring::REGEX:
492 if (!QuoteStringForRegex(replacement.value(), &new_piece))
493 FatalStringQuoteException(replacement.value());
494 break;
495 }
496 }
497 output_string_length += new_piece.size();
498 processed_sandbox_pieces.push_back(new_piece);
499 }
500
501 // Build final output string.
502 final_sandbox_profile_str->reserve(output_string_length);
503
504 for (std::vector<std::string>::iterator it = processed_sandbox_pieces.begin();
505 it != processed_sandbox_pieces.end();
506 ++it) {
507 final_sandbox_profile_str->append(*it);
508 }
509 return true;
510 }
511
512
513 // Turns on the OS X sandbox for this process. 436 // Turns on the OS X sandbox for this process.
514 437
515 // static 438 // static
516 bool Sandbox::EnableSandbox(int sandbox_type, 439 bool Sandbox::EnableSandbox(int sandbox_type,
517 const base::FilePath& allowed_dir) { 440 const base::FilePath& allowed_dir) {
518 // Sanity - currently only SANDBOX_TYPE_UTILITY supports a directory being 441 // Sanity - currently only SANDBOX_TYPE_UTILITY supports a directory being
519 // passed in. 442 // passed in.
520 if (sandbox_type < SANDBOX_TYPE_AFTER_LAST_TYPE && 443 if (sandbox_type < SANDBOX_TYPE_AFTER_LAST_TYPE &&
521 sandbox_type != SANDBOX_TYPE_UTILITY) { 444 sandbox_type != SANDBOX_TYPE_UTILITY) {
522 DCHECK(allowed_dir.empty()) 445 DCHECK(allowed_dir.empty())
523 << "Only SANDBOX_TYPE_UTILITY allows a custom directory parameter."; 446 << "Only SANDBOX_TYPE_UTILITY allows a custom directory parameter.";
524 } 447 }
525 448
526 NSString* sandbox_data = LoadSandboxTemplate(sandbox_type); 449 NSString* sandbox_data = LoadSandboxTemplate(sandbox_type);
527 if (!sandbox_data) { 450 if (!sandbox_data) {
528 return false; 451 return false;
529 } 452 }
530 453
531 SandboxVariableSubstitions substitutions; 454 SandboxCompiler compiler([sandbox_data UTF8String]);
532 if (!allowed_dir.empty()) { 455 if (!compiler.Init()) {
533 // Add the sandbox commands necessary to access the given directory. 456 return false;
534 // Note: this function must be called before PostProcessSandboxProfile()
535 // since the string it inserts contains variables that need substitution.
536 NSString* allowed_dir_sandbox_command =
537 BuildAllowDirectoryAccessSandboxString(allowed_dir, &substitutions);
538
539 if (allowed_dir_sandbox_command) { // May be nil if function fails.
540 sandbox_data = [sandbox_data
541 stringByReplacingOccurrencesOfString:@";ENABLE_DIRECTORY_ACCESS"
542 withString:allowed_dir_sandbox_command];
543 }
544 } 457 }
545 458
546 NSMutableArray* tokens_to_remove = [NSMutableArray array]; 459 if (!allowed_dir.empty()) {
460 // Add the sandbox parameters necessary to access the given directory.
461 base::FilePath allowed_dir_canonical = GetCanonicalSandboxPath(allowed_dir);
462 std::string regex;
463 if (!QuoteStringForRegex(allowed_dir_canonical.value(), &regex)) {
464 FatalStringQuoteException(allowed_dir_canonical.value());
465 return false;
466 }
467 compiler.InsertStringParam("PERMITTED_DIR", regex);
468 }
547 469
548 // Enable verbose logging if enabled on the command line. (See common.sb 470 // Enable verbose logging if enabled on the command line. (See common.sb
549 // for details). 471 // for details).
550 const base::CommandLine* command_line = 472 const base::CommandLine* command_line =
551 base::CommandLine::ForCurrentProcess(); 473 base::CommandLine::ForCurrentProcess();
552 bool enable_logging = 474 bool enable_logging =
553 command_line->HasSwitch(switches::kEnableSandboxLogging);; 475 command_line->HasSwitch(switches::kEnableSandboxLogging);;
554 if (enable_logging) { 476 compiler.InsertBooleanParam("ENABLE_LOGGING", enable_logging);
555 [tokens_to_remove addObject:@";ENABLE_LOGGING"];
556 }
557
558 bool lion_or_later = base::mac::IsOSLionOrLater();
559 477
560 // Without this, the sandbox will print a message to the system log every 478 // Without this, the sandbox will print a message to the system log every
561 // time it denies a request. This floods the console with useless spew. 479 // time it denies a request. This floods the console with useless spew.
562 if (!enable_logging) { 480 compiler.InsertBooleanParam("DISABLE_SANDBOX_DENIAL_LOGGING",
563 substitutions["DISABLE_SANDBOX_DENIAL_LOGGING"] = 481 !enable_logging);
564 SandboxSubstring("(with no-log)");
565 } else {
566 substitutions["DISABLE_SANDBOX_DENIAL_LOGGING"] = SandboxSubstring("");
567 }
568 482
569 // Splice the path of the user's home directory into the sandbox profile 483 // Splice the path of the user's home directory into the sandbox profile
570 // (see renderer.sb for details). 484 // (see renderer.sb for details).
571 std::string home_dir = [NSHomeDirectory() fileSystemRepresentation]; 485 std::string home_dir = [NSHomeDirectory() fileSystemRepresentation];
572 486
573 base::FilePath home_dir_canonical = 487 base::FilePath home_dir_canonical =
574 GetCanonicalSandboxPath(base::FilePath(home_dir)); 488 GetCanonicalSandboxPath(base::FilePath(home_dir));
575 489
576 substitutions["USER_HOMEDIR_AS_LITERAL"] = 490 std::string quoted_home_dir;
577 SandboxSubstring(home_dir_canonical.value(), 491 if (!QuotePlainString(home_dir_canonical.value(), &quoted_home_dir)) {
578 SandboxSubstring::LITERAL); 492 FatalStringQuoteException(home_dir_canonical.value());
579 493 return false;
580 if (lion_or_later) {
581 // >=10.7 Sandbox rules.
582 [tokens_to_remove addObject:@";10.7_OR_ABOVE"];
583 } 494 }
584 495
585 substitutions["COMPONENT_BUILD_WORKAROUND"] = SandboxSubstring(""); 496 compiler.InsertStringParam("USER_HOMEDIR_AS_LITERAL", quoted_home_dir);
497
498 bool lion_or_later = base::mac::IsOSLionOrLater();
499 compiler.InsertBooleanParam("LION_OR_LATER", lion_or_later);
500
586 #if defined(COMPONENT_BUILD) 501 #if defined(COMPONENT_BUILD)
587 // dlopen() fails without file-read-metadata access if the executable image 502 // dlopen() fails without file-read-metadata access if the executable image
588 // contains LC_RPATH load commands. The components build uses those. 503 // contains LC_RPATH load commands. The components build uses those.
589 // See http://crbug.com/127465 504 // See http://crbug.com/127465
590 if (base::mac::IsOSSnowLeopard()) { 505 if (base::mac::IsOSSnowLeopard()) {
591 base::FilePath bundle_executable = base::mac::NSStringToFilePath( 506 compiler.InsertBooleanParam("COMPONENT_BUILD_WORKAROUND", true);
592 [base::mac::MainBundle() executablePath]);
593 NSString* sandbox_command = AllowMetadataForPath(
594 GetCanonicalSandboxPath(bundle_executable));
595 substitutions["COMPONENT_BUILD_WORKAROUND"] =
596 SandboxSubstring(base::SysNSStringToUTF8(sandbox_command));
597 } 507 }
598 #endif 508 #endif
599 509
600 // All information needed to assemble the final profile has been collected.
601 // Merge it all together.
602 std::string final_sandbox_profile_str;
603 if (!PostProcessSandboxProfile(sandbox_data, tokens_to_remove, substitutions,
604 &final_sandbox_profile_str)) {
605 return false;
606 }
607
608 // Initialize sandbox. 510 // Initialize sandbox.
609 char* error_buff = NULL; 511 std::string error_str;
610 int error = sandbox_init(final_sandbox_profile_str.c_str(), 0, &error_buff); 512 int error = compiler.CompileAndApplyProfile(&error_str);
611 bool success = (error == 0 && error_buff == NULL); 513 bool success = (error == 0);
612 DLOG_IF(FATAL, !success) << "Failed to initialize sandbox: " 514 DLOG_IF(FATAL, !success) << "Failed to initialize sandbox: " << error << " "
613 << error 515 << error_str;
614 << " "
615 << error_buff;
616 sandbox_free_error(error_buff);
617 gSandboxIsActive = success; 516 gSandboxIsActive = success;
618 return success; 517 return success;
619 } 518 }
620 519
621 // static 520 // static
622 bool Sandbox::SandboxIsCurrentlyActive() { 521 bool Sandbox::SandboxIsCurrentlyActive() {
623 return gSandboxIsActive; 522 return gSandboxIsActive;
624 } 523 }
625 524
626 // static 525 // static
627 base::FilePath Sandbox::GetCanonicalSandboxPath(const base::FilePath& path) { 526 base::FilePath Sandbox::GetCanonicalSandboxPath(const base::FilePath& path) {
628 base::ScopedFD fd(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY))); 527 base::ScopedFD fd(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY)));
629 if (!fd.is_valid()) { 528 if (!fd.is_valid()) {
630 DPLOG(FATAL) << "GetCanonicalSandboxPath() failed for: " 529 DPLOG(FATAL) << "GetCanonicalSandboxPath() failed for: "
631 << path.value(); 530 << path.value();
632 return path; 531 return path;
633 } 532 }
634 533
635 base::FilePath::CharType canonical_path[MAXPATHLEN]; 534 base::FilePath::CharType canonical_path[MAXPATHLEN];
636 if (HANDLE_EINTR(fcntl(fd.get(), F_GETPATH, canonical_path)) != 0) { 535 if (HANDLE_EINTR(fcntl(fd.get(), F_GETPATH, canonical_path)) != 0) {
637 DPLOG(FATAL) << "GetCanonicalSandboxPath() failed for: " 536 DPLOG(FATAL) << "GetCanonicalSandboxPath() failed for: "
638 << path.value(); 537 << path.value();
639 return path; 538 return path;
640 } 539 }
641 540
642 return base::FilePath(canonical_path); 541 return base::FilePath(canonical_path);
643 } 542 }
644 543
645 } // namespace content 544 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698