summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorjeremy@chromium.org <jeremy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-12-03 15:36:08 +0000
committerjeremy@chromium.org <jeremy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-12-03 15:36:08 +0000
commit120be5d1b6455f4a97eaf560d12f0f78c8b1a107 (patch)
treeee6b8b128d227780501617474e76b73601d01bfe /chrome
parent780ae94178769a226f9e73f3776412edea60f850 (diff)
downloadchromium_src-120be5d1b6455f4a97eaf560d12f0f78c8b1a107.zip
chromium_src-120be5d1b6455f4a97eaf560d12f0f78c8b1a107.tar.gz
chromium_src-120be5d1b6455f4a97eaf560d12f0f78c8b1a107.tar.bz2
Add regex escaping code to Mac sandbox implementation and re-enable the utility process on OS X.
Other changes: * An error initializing the sandbox on OS X is now treated as fatal. * Improved error reporting for sandbox-related failures. BUG=26492,23837 TEST=Installing extensions and themes should still work on OS X. Review URL: http://codereview.chromium.org/434077 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@33682 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/app/chrome_dll_main.cc9
-rw-r--r--chrome/browser/extensions/extension_updater.cc5
-rw-r--r--chrome/browser/extensions/sandboxed_extension_unpacker.cc5
-rw-r--r--chrome/browser/utility.sb4
-rw-r--r--chrome/browser/utility_process_host.cc4
-rw-r--r--chrome/browser/web_resource/web_resource_service.cc5
-rwxr-xr-xchrome/chrome_tests.gypi1
-rw-r--r--chrome/common/sandbox_mac.mm206
-rw-r--r--chrome/common/sandbox_mac_unittest.mm244
9 files changed, 452 insertions, 31 deletions
diff --git a/chrome/app/chrome_dll_main.cc b/chrome/app/chrome_dll_main.cc
index 2217c8e..07ccf6d 100644
--- a/chrome/app/chrome_dll_main.cc
+++ b/chrome/app/chrome_dll_main.cc
@@ -554,8 +554,13 @@ int ChromeMain(int argc, char** argv) {
// On OS X the renderer sandbox needs to be initialized later in the startup
// sequence in RendererMainPlatformDelegate::PlatformInitialize().
if (process_type != switches::kRendererProcess &&
- process_type != switches::kExtensionProcess)
- sandbox_wrapper.InitializeSandbox(parsed_command_line, process_type);
+ process_type != switches::kExtensionProcess) {
+ bool sandbox_initialized_ok =
+ sandbox_wrapper.InitializeSandbox(parsed_command_line, process_type);
+ // Die if the sandbox can't be enabled.
+ CHECK(sandbox_initialized_ok) << "Error initializing sandbox for "
+ << process_type;
+ }
#endif // OS_MACOSX
startup_timer.Stop(); // End of Startup Time Measurement.
diff --git a/chrome/browser/extensions/extension_updater.cc b/chrome/browser/extensions/extension_updater.cc
index a618de9..f79dc7d 100644
--- a/chrome/browser/extensions/extension_updater.cc
+++ b/chrome/browser/extensions/extension_updater.cc
@@ -269,9 +269,8 @@ class SafeManifestParser : public UtilityProcessHost::Client {
bool use_utility_process = rdh &&
!CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess);
-#if defined(OS_POSIX)
- // TODO(port): Don't use a utility process on linux (crbug.com/22703) or
- // MacOS (crbug.com/8102) until problems related to autoupdate are fixed.
+#if defined(OS_LINUX)
+ // TODO(port): Don't use a utility process on linux (crbug.com/22703).
use_utility_process = false;
#endif
diff --git a/chrome/browser/extensions/sandboxed_extension_unpacker.cc b/chrome/browser/extensions/sandboxed_extension_unpacker.cc
index 46167ad..a3ece10 100644
--- a/chrome/browser/extensions/sandboxed_extension_unpacker.cc
+++ b/chrome/browser/extensions/sandboxed_extension_unpacker.cc
@@ -65,9 +65,8 @@ void SandboxedExtensionUnpacker::Start() {
bool use_utility_process = rdh_ &&
!CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess);
-#if defined(OS_POSIX)
- // TODO(port): Don't use a utility process on linux (crbug.com/22703) or
- // MacOS (crbug.com/8102) until problems related to autoupdate are fixed.
+#if defined(OS_LINUX)
+ // TODO(port): Don't use a utility process on linux (crbug.com/22703).
use_utility_process = false;
#endif
diff --git a/chrome/browser/utility.sb b/chrome/browser/utility.sb
index 291e677..3f88f1cb 100644
--- a/chrome/browser/utility.sb
+++ b/chrome/browser/utility.sb
@@ -36,5 +36,5 @@
; Needed for IPC on 10.6
;10.6_ONLY (allow ipc-posix-shm)
-; Enable full access to given directory.
-(allow file-read* file-write* (regex #"^DIR_TO_ALLOW_ACCESS"))
+; Enable full access to given directory if needed.
+;ENABLE_DIRECTORY_ACCESS (allow file-read* file-write* (regex #"DIR_TO_ALLOW_ACCESS"))
diff --git a/chrome/browser/utility_process_host.cc b/chrome/browser/utility_process_host.cc
index 9f7d647..352425a 100644
--- a/chrome/browser/utility_process_host.cc
+++ b/chrome/browser/utility_process_host.cc
@@ -55,9 +55,9 @@ FilePath UtilityProcessHost::GetUtilityProcessCmd() {
}
bool UtilityProcessHost::StartProcess(const FilePath& exposed_dir) {
-#if defined(OS_POSIX)
+#if defined(OS_LINUX)
// TODO(port): We should not reach here on Linux (crbug.com/22703).
- // (crbug.com/23837) covers enabling this on Linux/OS X.
+ // (crbug.com/23837) covers enabling this on Linux.
NOTREACHED();
return false;
#endif
diff --git a/chrome/browser/web_resource/web_resource_service.cc b/chrome/browser/web_resource/web_resource_service.cc
index c4ffa47..3d213f1 100644
--- a/chrome/browser/web_resource/web_resource_service.cc
+++ b/chrome/browser/web_resource/web_resource_service.cc
@@ -111,9 +111,8 @@ class WebResourceService::UnpackerClient
web_resource_service_->resource_dispatcher_host_ != NULL &&
!CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess);
-#if defined(OS_POSIX)
- // TODO(port): Don't use a utility process on linux (crbug.com/22703) or
- // MacOS (crbug.com/8102) until problems related to autoupdate are fixed.
+#if defined(OS_LINUX)
+ // TODO(port): Don't use a utility process on linux (crbug.com/22703).
use_utility_process = false;
#endif
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 63b7847..46d4a15 100755
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -759,6 +759,7 @@
'common/pref_service_unittest.cc',
'common/property_bag_unittest.cc',
'common/resource_dispatcher_unittest.cc',
+ 'common/sandbox_mac_unittest.mm',
'common/thumbnail_score_unittest.cc',
'common/time_format_unittest.cc',
'common/worker_thread_ticker_unittest.cc',
diff --git a/chrome/common/sandbox_mac.mm b/chrome/common/sandbox_mac.mm
index 6eac34c..ca8ac6c 100644
--- a/chrome/common/sandbox_mac.mm
+++ b/chrome/common/sandbox_mac.mm
@@ -13,17 +13,170 @@ extern "C" {
#include "base/basictypes.h"
#include "base/command_line.h"
-#include "base/json/string_escape.h"
+#include "base/file_util.h"
#include "base/mac_util.h"
#include "base/scoped_cftyperef.h"
#include "base/scoped_nsautorelease_pool.h"
#include "base/string16.h"
#include "base/sys_info.h"
#include "base/sys_string_conversions.h"
+#include "base/utf_string_conversions.h"
#include "chrome/common/chrome_switches.h"
+#include "unicode/uchar.h"
+
+namespace {
+
+// Try to escape |c| as a "SingleEscapeCharacter" (\n, etc). If successful,
+// returns true and appends the escape sequence to |dst|.
+bool EscapeSingleChar(char c, std::string* dst) {
+ const char *append = NULL;
+ switch (c) {
+ case '\b':
+ append = "\\b";
+ break;
+ case '\f':
+ append = "\\f";
+ break;
+ case '\n':
+ append = "\\n";
+ break;
+ case '\r':
+ append = "\\r";
+ break;
+ case '\t':
+ append = "\\t";
+ break;
+ case '\\':
+ append = "\\\\";
+ break;
+ case '"':
+ append = "\\\"";
+ break;
+ }
+
+ if (!append) {
+ return false;
+ }
+
+ dst->append(append);
+ return true;
+}
+
+} // namespace
namespace sandbox {
+// Escape |str_utf8| for use in a plain string variable in a sandbox
+// configuraton file. On return |dst| is set to the utf-8 encoded quoted
+// output.
+// Returns: true on success, false otherwise.
+bool QuotePlainString(const std::string& str_utf8, std::string* dst) {
+ dst->clear();
+
+ const char* src = str_utf8.c_str();
+ int32_t length = str_utf8.length();
+ int32_t position = 0;
+ while (position < length) {
+ UChar32 c;
+ U8_NEXT(src, position, length, c); // Macro increments |position|.
+ DCHECK_GE(c, 0);
+ if (c < 0)
+ return false;
+
+ if (c < 128) { // EscapeSingleChar only handles ASCII.
+ char as_char = static_cast<char>(c);
+ if (EscapeSingleChar(as_char, dst)) {
+ continue;
+ }
+ }
+
+ if (c < 32 || c > 126) {
+ // Any characters that aren't printable ASCII get the \u treatment.
+ unsigned int as_uint = static_cast<unsigned int>(c);
+ StringAppendF(dst, "\\u%04X", as_uint);
+ continue;
+ }
+
+ // If we got here we know that the character in question is strictly
+ // in the ASCII range so there's no need to do any kind of encoding
+ // conversion.
+ dst->push_back(static_cast<char>(c));
+ }
+ return true;
+}
+
+// Escape |str_utf8| for use in a regex literal in a sandbox
+// configuraton file. On return |dst| is set to the utf-8 encoded quoted
+// output.
+//
+// The implementation of this function is based on empirical testing of the
+// OS X sandbox on 10.5.8 & 10.6.2 which is undocumented and subject to change.
+//
+// Note: If str_utf8 contains any characters < 32 || >125 then the function
+// fails and false is returned.
+//
+// Returns: true on success, false otherwise.
+bool QuoteStringForRegex(const std::string& str_utf8, std::string* dst) {
+ // List of chars with special meaning to regex.
+ // This list is derived from http://perldoc.perl.org/perlre.html .
+ const char regex_special_chars[] = {
+ '\\',
+
+ // Metacharacters
+ '^',
+ '.',
+ '$',
+ '|',
+ '(',
+ ')',
+ '[',
+ ']',
+
+ // Quantifiers
+ '*',
+ '+',
+ '?',
+ '{',
+ '}',
+ };
+
+ // Anchor regex at start of path.
+ dst->assign("^");
+
+ const char* src = str_utf8.c_str();
+ int32_t length = str_utf8.length();
+ int32_t position = 0;
+ while (position < length) {
+ UChar32 c;
+ U8_NEXT(src, position, length, c); // Macro increments |position|.
+ DCHECK_GE(c, 0);
+ if (c < 0)
+ return false;
+
+ // The Mac sandbox regex parser only handles printable ASCII characters.
+ // 33 >= c <= 126
+ if (c < 32 || c > 125) {
+ return false;
+ }
+
+ for (size_t i = 0; i < arraysize(regex_special_chars); ++i) {
+ if (c == regex_special_chars[i]) {
+ dst->push_back('\\');
+ break;
+ }
+ }
+
+ dst->push_back(static_cast<char>(c));
+ }
+
+ // Make sure last element of path is interpreted as a directory. Leaving this
+ // off would allow access to files if they start with the same name as the
+ // directory.
+ dst->append("(/|$)");
+
+ return true;
+}
+
// Warm up System APIs that empirically need to be accessed before the Sandbox
// is turned on.
// This method is layed out in blocks, each one containing a separate function
@@ -91,12 +244,7 @@ bool EnableSandbox(SandboxProcessType sandbox_type,
if (sandbox_type != SANDBOX_TYPE_UTILITY) {
DCHECK(allowed_dir.empty())
<< "Only SANDBOX_TYPE_UTILITY allows a custom directory parameter.";
- } else {
- DCHECK(!allowed_dir.empty())
- << "SANDBOX_TYPE_UTILITY "
- << "needs a custom directory parameter, but an empty one was provided.";
}
-
// We use a custom sandbox definition file to lock things down as
// tightly as possible.
// TODO(jeremy): Look at using include syntax to unify common parts of sandbox
@@ -122,11 +270,12 @@ bool EnableSandbox(SandboxProcessType sandbox_type,
ofType:@"sb"];
NSString* sandbox_data = [NSString
stringWithContentsOfFile:sandbox_profile_path
- encoding:NSUTF8StringEncoding
- error:nil];
+ encoding:NSUTF8StringEncoding
+ error:nil];
if (!sandbox_data) {
- LOG(ERROR) << "Failed to find the sandbox profile on disk";
+ PLOG(ERROR) << "Failed to find the sandbox profile on disk "
+ << base::SysNSStringToUTF8(sandbox_profile_path);
return false;
}
@@ -140,10 +289,31 @@ bool EnableSandbox(SandboxProcessType sandbox_type,
}
if (!allowed_dir.empty()) {
- NSString* allowed_dir_ns = base::SysUTF8ToNSString(allowed_dir.value());
+ // 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).
+ FilePath allowed_dir_absolute(allowed_dir);
+ if (!file_util::AbsolutePath(&allowed_dir_absolute)) {
+ PLOG(ERROR) << "Failed to resolve absolute path";
+ return false;
+ }
+
+ std::string allowed_dir_escaped;
+ if (!QuoteStringForRegex(allowed_dir_absolute.value(),
+ &allowed_dir_escaped)) {
+ LOG(ERROR) << "Regex string quoting failed " << allowed_dir.value();
+ return false;
+ }
+ NSString* allowed_dir_escaped_ns = base::SysUTF8ToNSString(
+ allowed_dir_escaped.c_str());
+ sandbox_data = [sandbox_data
+ stringByReplacingOccurrencesOfString:@";ENABLE_DIRECTORY_ACCESS"
+ withString:@""];
sandbox_data = [sandbox_data
stringByReplacingOccurrencesOfString:@"DIR_TO_ALLOW_ACCESS"
- withString:allowed_dir_ns];
+ withString:allowed_dir_escaped_ns];
+
}
int32 major_version, minor_version, bugfix_version;
@@ -161,9 +331,12 @@ bool EnableSandbox(SandboxProcessType sandbox_type,
// for this "subdir" is only supported on 10.6.
// If we ever need this on pre-10.6 OSs then we'll have to rethink the
// surrounding sandbox syntax.
- string16 home_dir = base::SysNSStringToUTF16(NSHomeDirectory());
+ std::string home_dir = base::SysNSStringToUTF8(NSHomeDirectory());
std::string home_dir_escaped;
- base::JsonDoubleQuote(home_dir, false, &home_dir_escaped);
+ if (!QuotePlainString(home_dir, &home_dir_escaped)) {
+ LOG(ERROR) << "Sandbox string quoting failed";
+ return false;
+ }
NSString* home_dir_escaped_ns = base::SysUTF8ToNSString(home_dir_escaped);
sandbox_data = [sandbox_data
stringByReplacingOccurrencesOfString:@"USER_HOMEDIR"
@@ -173,9 +346,10 @@ bool EnableSandbox(SandboxProcessType sandbox_type,
char* error_buff = NULL;
int error = sandbox_init([sandbox_data UTF8String], 0, &error_buff);
bool success = (error == 0 && error_buff == NULL);
- if (error == -1) {
- LOG(ERROR) << "Failed to Initialize Sandbox: " << error_buff;
- }
+ LOG_IF(ERROR, !success) << "Failed to initialize sandbox: "
+ << error
+ << " "
+ << error_buff;
sandbox_free_error(error_buff);
return success;
}
diff --git a/chrome/common/sandbox_mac_unittest.mm b/chrome/common/sandbox_mac_unittest.mm
new file mode 100644
index 0000000..6f469f1
--- /dev/null
+++ b/chrome/common/sandbox_mac_unittest.mm
@@ -0,0 +1,244 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <Cocoa/Cocoa.h>
+#include <dirent.h>
+
+extern "C" {
+#include <sandbox.h>
+}
+
+#include "base/file_util.h"
+#include "base/file_path.h"
+#include "base/multiprocess_test.h"
+#include "base/string_util.h"
+#include "base/sys_string_conversions.h"
+#include "chrome/common/sandbox_mac.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+bool QuotePlainString(const std::string& str_utf8, std::string* dst);
+bool QuoteStringForRegex(const std::string& str_utf8, std::string* dst);
+
+} // namespace sandbox
+
+static const char* kSandboxAccessPathKey = "sandbox_dir";
+
+class MacSandboxTest : public MultiProcessTest {
+ public:
+ bool CheckSandbox(std::string directory_to_try) {
+ setenv(kSandboxAccessPathKey, directory_to_try.c_str(), 1);
+ base::ProcessHandle child_process = SpawnChild(L"mac_sandbox_path_access");
+ int code = -1;
+ if (!base::WaitForExitCode(child_process, &code)) {
+ LOG(WARNING) << "base::WaitForExitCode failed";
+ return false;
+ }
+ return code == 0;
+ }
+};
+
+TEST_F(MacSandboxTest, StringEscape) {
+ using sandbox::QuotePlainString;
+
+ const struct string_escape_test_data {
+ const char* to_escape;
+ const char* escaped;
+ } string_escape_cases[] = {
+ {"", ""},
+ {"\b\f\n\r\t\\\"", "\\b\\f\\n\\r\\t\\\\\\\""},
+ {"/'", "/'"},
+ {"sandwich", "sandwich"},
+ {"(sandwich)", "(sandwich)"},
+ {"^\u2135.\u2136$", "^\\u2135.\\u2136$"},
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(string_escape_cases); ++i) {
+ std::string out;
+ std::string in(string_escape_cases[i].to_escape);
+ EXPECT_TRUE(QuotePlainString(in, &out));
+ EXPECT_EQ(string_escape_cases[i].escaped, out);
+ }
+}
+
+TEST_F(MacSandboxTest, RegexEscape) {
+ using sandbox::QuoteStringForRegex;
+
+ const std::string kSandboxEscapeSuffix("(/|$)");
+ const struct regex_test_data {
+ const wchar_t *to_escape;
+ const char* escaped;
+ } regex_cases[] = {
+ {L"", ""},
+ {L"/'", "/'"}, // / & ' characters don't need escaping.
+ {L"sandwich", "sandwich"},
+ {L"(sandwich)", "\\(sandwich\\)"},
+ };
+
+ // Check that all characters whose values are smaller than 32 [1F] are
+ // rejected by the regex escaping code.
+ {
+ std::string out;
+ char fail_string[] = {31, 0};
+ char ok_string[] = {32, 0};
+ EXPECT_FALSE(QuoteStringForRegex(fail_string, &out));
+ EXPECT_TRUE(QuoteStringForRegex(ok_string, &out));
+ }
+
+ // Check that all characters whose values are larger than 126 [7E] are
+ // rejected by the regex escaping code.
+ {
+ std::string out;
+ EXPECT_TRUE(QuoteStringForRegex("}", &out)); // } == 0x7D == 125
+ EXPECT_FALSE(QuoteStringForRegex("~", &out)); // ~ == 0x7E == 126
+ EXPECT_FALSE(QuoteStringForRegex(WideToUTF8(L"^\u2135.\u2136$"), &out));
+ }
+
+ {
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(regex_cases); ++i) {
+ std::string out;
+ std::string in = WideToUTF8(regex_cases[i].to_escape);
+ EXPECT_TRUE(QuoteStringForRegex(in, &out));
+ std::string expected("^");
+ expected.append(regex_cases[i].escaped);
+ expected.append(kSandboxEscapeSuffix);
+ EXPECT_EQ(expected, out);
+ }
+ }
+
+ {
+ std::string in_utf8("\\^.$|()[]*+?{}");
+ std::string expected;
+ expected.push_back('^');
+ for (size_t i = 0; i < in_utf8.length(); ++i) {
+ expected.push_back('\\');
+ expected.push_back(in_utf8[i]);
+ }
+ expected.append(kSandboxEscapeSuffix);
+
+ std::string out;
+ EXPECT_TRUE(QuoteStringForRegex(in_utf8, &out));
+ EXPECT_EQ(expected, out);
+
+ }
+}
+
+// A class to handle auto-deleting a directory.
+class ScopedDirectoryDelete {
+ public:
+ inline void operator()(FilePath* x) const {
+ if (x) {
+ file_util::Delete(*x, true);
+ }
+ }
+};
+
+typedef scoped_ptr_malloc<FilePath, ScopedDirectoryDelete> ScopedDirectory;
+
+TEST_F(MacSandboxTest, SandboxAccess) {
+ FilePath tmp_dir;
+ ASSERT_TRUE(file_util::CreateNewTempDirectory("", &tmp_dir));
+ // This step is important on OS X since the sandbox only understands "real"
+ // paths and the paths CreateNewTempDirectory() returns are empirically in
+ // /var which is a symlink to /private/var .
+ ASSERT_TRUE(file_util::AbsolutePath(&tmp_dir));
+ ScopedDirectory cleanup(&tmp_dir);
+
+ const char* sandbox_dir_cases[] = {
+ "simple_dir_name",
+ "^hello++ $", // Regex.
+ "\\^.$|()[]*+?{}", // All regex characters.
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(sandbox_dir_cases); ++i) {
+ const char* sandbox_dir_name = sandbox_dir_cases[i];
+ FilePath sandbox_dir = tmp_dir.Append(sandbox_dir_name);
+ ASSERT_TRUE(file_util::CreateDirectory(sandbox_dir));
+ ScopedDirectory cleanup_sandbox(&sandbox_dir);
+ EXPECT_TRUE(CheckSandbox(sandbox_dir.value()));
+ }
+}
+
+MULTIPROCESS_TEST_MAIN(mac_sandbox_path_access) {
+ char *sandbox_allowed_dir = getenv(kSandboxAccessPathKey);
+ if (!sandbox_allowed_dir)
+ return -1;
+
+ // Build up a sandbox profile that only allows access to DIR_TO_ALLOW_ACCESS.
+ NSString *sandbox_profile =
+ @"(version 1)" \
+ "(deny default)" \
+ "(allow signal (target self))" \
+ "(allow sysctl-read)" \
+ "(allow file-read-metadata)" \
+ "(allow file-read* file-write* (regex #\"DIR_TO_ALLOW_ACCESS\"))";
+
+ std::string allowed_dir(sandbox_allowed_dir);
+ std::string allowed_dir_escaped;
+ if (!sandbox::QuoteStringForRegex(allowed_dir, &allowed_dir_escaped)) {
+ LOG(ERROR) << "Regex string quoting failed " << allowed_dir;
+ return -1;
+ }
+ NSString* allowed_dir_escaped_ns = base::SysUTF8ToNSString(
+ allowed_dir_escaped.c_str());
+ sandbox_profile = [sandbox_profile
+ stringByReplacingOccurrencesOfString:@"DIR_TO_ALLOW_ACCESS"
+ withString:allowed_dir_escaped_ns];
+ // Enable Sandbox.
+ char* error_buff = NULL;
+ int error = sandbox_init([sandbox_profile UTF8String], 0, &error_buff);
+ if (error == -1) {
+ LOG(ERROR) << "Failed to Initialize Sandbox: " << error_buff;
+ return -1;
+ }
+ sandbox_free_error(error_buff);
+
+ // Test Sandbox.
+
+ // We should be able to list the contents of the sandboxed directory.
+ DIR *file_list = NULL;
+ file_list = opendir(sandbox_allowed_dir);
+ if (!file_list) {
+ PLOG(ERROR) << "Sandbox overly restrictive: call to opendir("
+ << sandbox_allowed_dir
+ << ") failed";
+ return -1;
+ }
+ closedir(file_list);
+
+ // Test restrictions on accessing files.
+ FilePath allowed_dir_path(sandbox_allowed_dir);
+ FilePath allowed_file = allowed_dir_path.Append("ok_to_write");
+ FilePath denied_file1 = allowed_dir_path.DirName().Append("cant_access");
+
+ // Try to write a file who's name has the same prefix as the directory we
+ // allow access to.
+ FilePath basename = allowed_dir_path.BaseName();
+ std::string tricky_filename = basename.value() + "123";
+ FilePath denied_file2 = allowed_dir_path.DirName().Append(tricky_filename);
+
+ if (open(allowed_file.value().c_str(), O_WRONLY | O_CREAT) <= 0) {
+ PLOG(ERROR) << "Sandbox overly restrictive: failed to write ("
+ << allowed_file.value()
+ << ")";
+ return -1;
+ }
+
+ if (open(denied_file1.value().c_str(), O_WRONLY | O_CREAT) > 0) {
+ PLOG(ERROR) << "Sandbox breach: was able to write ("
+ << denied_file1.value()
+ << ")";
+ return -1;
+ }
+
+ if (open(denied_file2.value().c_str(), O_WRONLY | O_CREAT) > 0) {
+ PLOG(ERROR) << "Sandbox breach: was able to write ("
+ << denied_file2.value()
+ << ")";
+ return -1;
+ }
+
+ return 0;
+}