| Index: content/common/sandbox_mac.mm
|
| diff --git a/content/common/sandbox_mac.mm b/content/common/sandbox_mac.mm
|
| index 977823e962dec6ec9eea9886b84c9118c515cd9d..a13aaf773727135c8f6c4bba513a5dc5db7951e2 100644
|
| --- a/content/common/sandbox_mac.mm
|
| +++ b/content/common/sandbox_mac.mm
|
| @@ -43,15 +43,6 @@
|
| extern "C" {
|
| void CGSSetDenyWindowServerConnections(bool);
|
| void CGSShutdownServerConnections();
|
| -
|
| -void* sandbox_create_params();
|
| -int sandbox_set_param(void* params, const char* key, const char* value);
|
| -void* sandbox_compile_string(const char* profile_str,
|
| - void* params,
|
| - char** error);
|
| -int sandbox_apply(void* profile);
|
| -void sandbox_free_params(void* params);
|
| -void sandbox_free_profile(void* profile);
|
| };
|
|
|
| namespace content {
|
| @@ -125,59 +116,37 @@
|
|
|
| } // namespace
|
|
|
| -SandboxCompiler::SandboxCompiler(const std::string& profile_str)
|
| - : params_map_(), profile_str_(profile_str) {
|
| -}
|
| -
|
| -SandboxCompiler::~SandboxCompiler() {
|
| -}
|
| -
|
| -bool SandboxCompiler::InsertBooleanParam(const std::string& key, bool value) {
|
| - return params_map_.insert(std::make_pair(key, value ? "TRUE" : "FALSE"))
|
| - .second;
|
| -}
|
| -
|
| -bool SandboxCompiler::InsertStringParam(const std::string& key,
|
| - const std::string& value) {
|
| - return params_map_.insert(std::make_pair(key, value)).second;
|
| -}
|
| -
|
| -void SandboxCompiler::FreeSandboxResources(void* profile,
|
| - void* params,
|
| - char* error) {
|
| - if (error)
|
| - sandbox_free_error(error);
|
| - if (params)
|
| - sandbox_free_params(params);
|
| - if (profile)
|
| - sandbox_free_profile(profile);
|
| -}
|
| -
|
| -bool SandboxCompiler::CompileAndApplyProfile(std::string* error) {
|
| - char* error_internal = nullptr;
|
| - void* profile = nullptr;
|
| - void* params = nullptr;
|
| -
|
| - if (!params_map_.empty()) {
|
| - params = sandbox_create_params();
|
| - if (!params)
|
| - return false;
|
| -
|
| - for (const auto& kv : params_map_)
|
| - sandbox_set_param(params, kv.first.c_str(), kv.second.c_str());
|
| - }
|
| -
|
| - profile =
|
| - sandbox_compile_string(profile_str_.c_str(), params, &error_internal);
|
| - if (!profile) {
|
| - error->assign(error_internal);
|
| - FreeSandboxResources(profile, params, error_internal);
|
| - return false;
|
| - }
|
| -
|
| - int result = sandbox_apply(profile);
|
| - FreeSandboxResources(profile, params, error_internal);
|
| - return result == 0;
|
| +// static
|
| +NSString* Sandbox::AllowMetadataForPath(const base::FilePath& allowed_path) {
|
| + // Collect a list of all parent directories.
|
| + base::FilePath last_path = allowed_path;
|
| + std::vector<base::FilePath> subpaths;
|
| + for (base::FilePath path = allowed_path;
|
| + path.value() != last_path.value();
|
| + path = path.DirName()) {
|
| + subpaths.push_back(path);
|
| + last_path = path;
|
| + }
|
| +
|
| + // Iterate through all parents and allow stat() on them explicitly.
|
| + NSString* sandbox_command = @"(allow file-read-metadata ";
|
| + for (std::vector<base::FilePath>::reverse_iterator i = subpaths.rbegin();
|
| + i != subpaths.rend();
|
| + ++i) {
|
| + std::string subdir_escaped;
|
| + if (!QuotePlainString(i->value(), &subdir_escaped)) {
|
| + FatalStringQuoteException(i->value());
|
| + return nil;
|
| + }
|
| +
|
| + NSString* subdir_escaped_ns =
|
| + base::SysUTF8ToNSString(subdir_escaped.c_str());
|
| + sandbox_command =
|
| + [sandbox_command stringByAppendingFormat:@"(literal \"%@\")",
|
| + subdir_escaped_ns];
|
| + }
|
| +
|
| + return [sandbox_command stringByAppendingString:@")"];
|
| }
|
|
|
| // static
|
| @@ -380,6 +349,41 @@
|
| }
|
| }
|
|
|
| +// static
|
| +NSString* Sandbox::BuildAllowDirectoryAccessSandboxString(
|
| + const base::FilePath& allowed_dir,
|
| + SandboxVariableSubstitions* substitutions) {
|
| + // A whitelist is used to determine which directories can be statted
|
| + // This means that in the case of an /a/b/c/d/ directory, we may be able to
|
| + // stat the leaf directory, but not its parent.
|
| + // The extension code in Chrome calls realpath() which fails if it can't call
|
| + // stat() on one of the parent directories in the path.
|
| + // The solution to this is to allow statting the parent directories themselves
|
| + // but not their contents. We need to add a separate rule for each parent
|
| + // directory.
|
| +
|
| + // The sandbox only understands "real" paths. This resolving step is
|
| + // needed so the caller doesn't need to worry about things like /var
|
| + // being a link to /private/var (like in the paths CreateNewTempDirectory()
|
| + // returns).
|
| + base::FilePath allowed_dir_canonical = GetCanonicalSandboxPath(allowed_dir);
|
| +
|
| + NSString* sandbox_command = AllowMetadataForPath(allowed_dir_canonical);
|
| + sandbox_command = [sandbox_command
|
| + substringToIndex:[sandbox_command length] - 1]; // strip trailing ')'
|
| +
|
| + // Finally append the leaf directory. Unlike its parents (for which only
|
| + // stat() should be allowed), the leaf directory needs full access.
|
| + (*substitutions)["ALLOWED_DIR"] =
|
| + SandboxSubstring(allowed_dir_canonical.value(),
|
| + SandboxSubstring::REGEX);
|
| + sandbox_command =
|
| + [sandbox_command
|
| + stringByAppendingString:@") (allow file-read* file-write*"
|
| + " (regex #\"@ALLOWED_DIR@\") )"];
|
| + return sandbox_command;
|
| +}
|
| +
|
| // Load the appropriate template for the given sandbox type.
|
| // Returns the template as an NSString or nil on error.
|
| NSString* LoadSandboxTemplate(int sandbox_type) {
|
| @@ -437,6 +441,75 @@
|
| return [common_sandbox_prefix_data stringByAppendingString:sandbox_data];
|
| }
|
|
|
| +// static
|
| +bool Sandbox::PostProcessSandboxProfile(
|
| + NSString* sandbox_template,
|
| + NSArray* comments_to_remove,
|
| + SandboxVariableSubstitions& substitutions,
|
| + std::string *final_sandbox_profile_str) {
|
| + NSString* sandbox_data = [[sandbox_template copy] autorelease];
|
| +
|
| + // Remove comments, e.g. ;10.7_OR_ABOVE .
|
| + for (NSString* to_remove in comments_to_remove) {
|
| + sandbox_data = [sandbox_data stringByReplacingOccurrencesOfString:to_remove
|
| + withString:@""];
|
| + }
|
| +
|
| + // Split string on "@" characters.
|
| + std::vector<std::string> raw_sandbox_pieces;
|
| + if (Tokenize([sandbox_data UTF8String], "@", &raw_sandbox_pieces) == 0) {
|
| + DLOG(FATAL) << "Bad Sandbox profile, should contain at least one token ("
|
| + << [sandbox_data UTF8String]
|
| + << ")";
|
| + return false;
|
| + }
|
| +
|
| + // Iterate over string pieces and substitute variables, escaping as necessary.
|
| + size_t output_string_length = 0;
|
| + std::vector<std::string> processed_sandbox_pieces(raw_sandbox_pieces.size());
|
| + for (std::vector<std::string>::iterator it = raw_sandbox_pieces.begin();
|
| + it != raw_sandbox_pieces.end();
|
| + ++it) {
|
| + std::string new_piece;
|
| + SandboxVariableSubstitions::iterator replacement_it =
|
| + substitutions.find(*it);
|
| + if (replacement_it == substitutions.end()) {
|
| + new_piece = *it;
|
| + } else {
|
| + // Found something to substitute.
|
| + SandboxSubstring& replacement = replacement_it->second;
|
| + switch (replacement.type()) {
|
| + case SandboxSubstring::PLAIN:
|
| + new_piece = replacement.value();
|
| + break;
|
| +
|
| + case SandboxSubstring::LITERAL:
|
| + if (!QuotePlainString(replacement.value(), &new_piece))
|
| + FatalStringQuoteException(replacement.value());
|
| + break;
|
| +
|
| + case SandboxSubstring::REGEX:
|
| + if (!QuoteStringForRegex(replacement.value(), &new_piece))
|
| + FatalStringQuoteException(replacement.value());
|
| + break;
|
| + }
|
| + }
|
| + output_string_length += new_piece.size();
|
| + processed_sandbox_pieces.push_back(new_piece);
|
| + }
|
| +
|
| + // Build final output string.
|
| + final_sandbox_profile_str->reserve(output_string_length);
|
| +
|
| + for (std::vector<std::string>::iterator it = processed_sandbox_pieces.begin();
|
| + it != processed_sandbox_pieces.end();
|
| + ++it) {
|
| + final_sandbox_profile_str->append(*it);
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +
|
| // Turns on the OS X sandbox for this process.
|
|
|
| // static
|
| @@ -455,19 +528,22 @@
|
| return false;
|
| }
|
|
|
| - SandboxCompiler compiler([sandbox_data UTF8String]);
|
| -
|
| + SandboxVariableSubstitions substitutions;
|
| if (!allowed_dir.empty()) {
|
| - // Add the sandbox parameters necessary to access the given directory.
|
| - base::FilePath allowed_dir_canonical = GetCanonicalSandboxPath(allowed_dir);
|
| - std::string regex;
|
| - if (!QuoteStringForRegex(allowed_dir_canonical.value(), ®ex)) {
|
| - FatalStringQuoteException(allowed_dir_canonical.value());
|
| - return false;
|
| - }
|
| - if (!compiler.InsertStringParam("PERMITTED_DIR", regex))
|
| - return false;
|
| - }
|
| + // Add the sandbox commands necessary to access the given directory.
|
| + // Note: this function must be called before PostProcessSandboxProfile()
|
| + // since the string it inserts contains variables that need substitution.
|
| + NSString* allowed_dir_sandbox_command =
|
| + BuildAllowDirectoryAccessSandboxString(allowed_dir, &substitutions);
|
| +
|
| + if (allowed_dir_sandbox_command) { // May be nil if function fails.
|
| + sandbox_data = [sandbox_data
|
| + stringByReplacingOccurrencesOfString:@";ENABLE_DIRECTORY_ACCESS"
|
| + withString:allowed_dir_sandbox_command];
|
| + }
|
| + }
|
| +
|
| + NSMutableArray* tokens_to_remove = [NSMutableArray array];
|
|
|
| // Enable verbose logging if enabled on the command line. (See common.sb
|
| // for details).
|
| @@ -475,14 +551,20 @@
|
| base::CommandLine::ForCurrentProcess();
|
| bool enable_logging =
|
| command_line->HasSwitch(switches::kEnableSandboxLogging);;
|
| - if (!compiler.InsertBooleanParam("ENABLE_LOGGING", enable_logging))
|
| - return false;
|
| + if (enable_logging) {
|
| + [tokens_to_remove addObject:@";ENABLE_LOGGING"];
|
| + }
|
| +
|
| + bool lion_or_later = base::mac::IsOSLionOrLater();
|
|
|
| // 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("DISABLE_SANDBOX_DENIAL_LOGGING",
|
| - !enable_logging))
|
| - return false;
|
| + if (!enable_logging) {
|
| + substitutions["DISABLE_SANDBOX_DENIAL_LOGGING"] =
|
| + SandboxSubstring("(with no-log)");
|
| + } else {
|
| + substitutions["DISABLE_SANDBOX_DENIAL_LOGGING"] = SandboxSubstring("");
|
| + }
|
|
|
| // Splice the path of the user's home directory into the sandbox profile
|
| // (see renderer.sb for details).
|
| @@ -491,33 +573,47 @@
|
| base::FilePath home_dir_canonical =
|
| GetCanonicalSandboxPath(base::FilePath(home_dir));
|
|
|
| - std::string quoted_home_dir;
|
| - if (!QuotePlainString(home_dir_canonical.value(), "ed_home_dir)) {
|
| - FatalStringQuoteException(home_dir_canonical.value());
|
| - return false;
|
| - }
|
| -
|
| - if (!compiler.InsertStringParam("USER_HOMEDIR_AS_LITERAL", quoted_home_dir))
|
| - return false;
|
| -
|
| - bool lion_or_later = base::mac::IsOSLionOrLater();
|
| - if (!compiler.InsertBooleanParam("LION_OR_LATER", lion_or_later))
|
| - return false;
|
| -
|
| + substitutions["USER_HOMEDIR_AS_LITERAL"] =
|
| + SandboxSubstring(home_dir_canonical.value(),
|
| + SandboxSubstring::LITERAL);
|
| +
|
| + if (lion_or_later) {
|
| + // >=10.7 Sandbox rules.
|
| + [tokens_to_remove addObject:@";10.7_OR_ABOVE"];
|
| + }
|
| +
|
| + substitutions["COMPONENT_BUILD_WORKAROUND"] = SandboxSubstring("");
|
| #if defined(COMPONENT_BUILD)
|
| // dlopen() fails without file-read-metadata access if the executable image
|
| // contains LC_RPATH load commands. The components build uses those.
|
| // See http://crbug.com/127465
|
| if (base::mac::IsOSSnowLeopard()) {
|
| - if (!compiler.InsertBooleanParam("COMPONENT_BUILD_WORKAROUND", true))
|
| - return false;
|
| + base::FilePath bundle_executable = base::mac::NSStringToFilePath(
|
| + [base::mac::MainBundle() executablePath]);
|
| + NSString* sandbox_command = AllowMetadataForPath(
|
| + GetCanonicalSandboxPath(bundle_executable));
|
| + substitutions["COMPONENT_BUILD_WORKAROUND"] =
|
| + SandboxSubstring(base::SysNSStringToUTF8(sandbox_command));
|
| }
|
| #endif
|
|
|
| + // All information needed to assemble the final profile has been collected.
|
| + // Merge it all together.
|
| + std::string final_sandbox_profile_str;
|
| + if (!PostProcessSandboxProfile(sandbox_data, tokens_to_remove, substitutions,
|
| + &final_sandbox_profile_str)) {
|
| + return false;
|
| + }
|
| +
|
| // Initialize sandbox.
|
| - std::string error_str;
|
| - bool success = compiler.CompileAndApplyProfile(&error_str);
|
| - DLOG_IF(FATAL, !success) << "Failed to initialize sandbox: " << error_str;
|
| + char* error_buff = NULL;
|
| + int error = sandbox_init(final_sandbox_profile_str.c_str(), 0, &error_buff);
|
| + bool success = (error == 0 && error_buff == NULL);
|
| + DLOG_IF(FATAL, !success) << "Failed to initialize sandbox: "
|
| + << error
|
| + << " "
|
| + << error_buff;
|
| + sandbox_free_error(error_buff);
|
| gSandboxIsActive = success;
|
| return success;
|
| }
|
|
|