diff options
Diffstat (limited to 'chrome/common/sandbox_mac_diraccess_unittest.mm')
| -rw-r--r-- | chrome/common/sandbox_mac_diraccess_unittest.mm | 129 |
1 files changed, 90 insertions, 39 deletions
diff --git a/chrome/common/sandbox_mac_diraccess_unittest.mm b/chrome/common/sandbox_mac_diraccess_unittest.mm index 5804361..fed540d 100644 --- a/chrome/common/sandbox_mac_diraccess_unittest.mm +++ b/chrome/common/sandbox_mac_diraccess_unittest.mm @@ -18,18 +18,16 @@ extern "C" { #include "testing/gtest/include/gtest/gtest.h" #include "testing/multiprocess_func_list.h" -// Tests to exercise directory-access-related restrictions of Mac sandbox. - -namespace sandbox { - -bool QuotePlainString(const std::string& str_utf8, std::string* dst); -bool QuoteStringForRegex(const std::string& str_utf8, std::string* dst); - -} // namespace sandbox - namespace { static const char* kSandboxAccessPathKey = "sandbox_dir"; +static const char* kDeniedSuffix = "_denied"; + +} // namespace + +// Tests need to be in the same namespace as the sandbox::Sandbox class to be +// useable with FRIEND_TEST() declaration. +namespace sandbox { class MacDirAccessSandboxTest : public base::MultiProcessTest { public: @@ -47,8 +45,6 @@ class MacDirAccessSandboxTest : public base::MultiProcessTest { }; TEST_F(MacDirAccessSandboxTest, StringEscape) { - using sandbox::QuotePlainString; - const struct string_escape_test_data { const char* to_escape; const char* escaped; @@ -64,14 +60,12 @@ TEST_F(MacDirAccessSandboxTest, StringEscape) { 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_TRUE(Sandbox::QuotePlainString(in, &out)); EXPECT_EQ(string_escape_cases[i].escaped, out); } } TEST_F(MacDirAccessSandboxTest, RegexEscape) { - using sandbox::QuoteStringForRegex; - const std::string kSandboxEscapeSuffix("(/|$)"); const struct regex_test_data { const wchar_t *to_escape; @@ -89,24 +83,25 @@ TEST_F(MacDirAccessSandboxTest, RegexEscape) { 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)); + EXPECT_FALSE(Sandbox::QuoteStringForRegex(fail_string, &out)); + EXPECT_TRUE(Sandbox::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)); + EXPECT_TRUE(Sandbox::QuoteStringForRegex("}", &out)); // } == 0x7D == 125 + EXPECT_FALSE(Sandbox::QuoteStringForRegex("~", &out)); // ~ == 0x7E == 126 + EXPECT_FALSE( + Sandbox::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)); + EXPECT_TRUE(Sandbox::QuoteStringForRegex(in, &out)); std::string expected("^"); expected.append(regex_cases[i].escaped); expected.append(kSandboxEscapeSuffix); @@ -125,7 +120,7 @@ TEST_F(MacDirAccessSandboxTest, RegexEscape) { expected.append(kSandboxEscapeSuffix); std::string out; - EXPECT_TRUE(QuoteStringForRegex(in_utf8, &out)); + EXPECT_TRUE(Sandbox::QuoteStringForRegex(in_utf8, &out)); EXPECT_EQ(expected, out); } @@ -143,14 +138,15 @@ class ScopedDirectoryDelete { typedef scoped_ptr_malloc<FilePath, ScopedDirectoryDelete> ScopedDirectory; -// Crashy, http://crbug.com/56765. -TEST_F(MacDirAccessSandboxTest, DISABLED_SandboxAccess) { +TEST_F(MacDirAccessSandboxTest, SandboxAccess) { + using file_util::CreateDirectory; + 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 . - sandbox::GetCanonicalSandboxPath(&tmp_dir); + Sandbox::GetCanonicalSandboxPath(&tmp_dir); ScopedDirectory cleanup(&tmp_dir); const char* sandbox_dir_cases[] = { @@ -162,8 +158,18 @@ TEST_F(MacDirAccessSandboxTest, DISABLED_SandboxAccess) { 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)); + ASSERT_TRUE(CreateDirectory(sandbox_dir)); ScopedDirectory cleanup_sandbox(&sandbox_dir); + + // Create a sibling directory of the sandbox dir, whose name has sandbox dir + // as a substring but to which access is denied. + std::string sibling_sandbox_dir_name_denied = + std::string(sandbox_dir_cases[i]) + kDeniedSuffix; + FilePath sibling_sandbox_dir = tmp_dir.Append( + sibling_sandbox_dir_name_denied.c_str()); + ASSERT_TRUE(CreateDirectory(sibling_sandbox_dir)); + ScopedDirectory cleanup_sandbox_sibling(&sibling_sandbox_dir); + EXPECT_TRUE(CheckSandbox(sandbox_dir.value())); } } @@ -173,29 +179,36 @@ MULTIPROCESS_TEST_MAIN(mac_sandbox_path_access) { if (!sandbox_allowed_dir) return -1; - // Build up a sandbox profile that only allows access to DIR_TO_ALLOW_ACCESS. + // Build up a sandbox profile that only allows access to a single directory. 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\"))"; + ";ENABLE_DIRECTORY_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; + Sandbox::SandboxVariableSubstitions substitutions; + NSString* allow_dir_sandbox_code = + Sandbox::BuildAllowDirectoryAccessSandboxString( + FilePath(sandbox_allowed_dir), + &substitutions); + sandbox_profile = [sandbox_profile + stringByReplacingOccurrencesOfString:@";ENABLE_DIRECTORY_ACCESS" + withString:allow_dir_sandbox_code]; + + std::string final_sandbox_profile_str; + if (!Sandbox::PostProcessSandboxProfile(sandbox_profile, + [NSArray array], + substitutions, + &final_sandbox_profile_str)) { + LOG(ERROR) << "Call to PostProcessSandboxProfile() failed"; 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); + int error = sandbox_init(final_sandbox_profile_str.c_str(), 0, &error_buff); if (error == -1) { LOG(ERROR) << "Failed to Initialize Sandbox: " << error_buff; return -1; @@ -223,8 +236,9 @@ MULTIPROCESS_TEST_MAIN(mac_sandbox_path_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(); + FilePath allowed_parent_dir = allowed_dir_path.DirName(); std::string tricky_filename = basename.value() + "123"; - FilePath denied_file2 = allowed_dir_path.DirName().Append(tricky_filename); + FilePath denied_file2 = allowed_parent_dir.Append(tricky_filename); if (open(allowed_file.value().c_str(), O_WRONLY | O_CREAT) <= 0) { PLOG(ERROR) << "Sandbox overly restrictive: failed to write (" @@ -233,6 +247,43 @@ MULTIPROCESS_TEST_MAIN(mac_sandbox_path_access) { return -1; } + // Test that we deny access to a sibling of the sandboxed directory whose + // name has the sandboxed directory name as a substring. e.g. if the sandbox + // directory is /foo/baz then test /foo/baz_denied. + { + struct stat tmp_stat_info; + std::string denied_sibling = + std::string(sandbox_allowed_dir) + kDeniedSuffix; + if (stat(denied_sibling.c_str(), &tmp_stat_info) > 0) { + PLOG(ERROR) << "Sandbox breach: was able to stat (" + << denied_sibling.c_str() + << ")"; + return -1; + } + } + + // Test that we can stat parent directories of the "allowed" directory. + { + struct stat tmp_stat_info; + if (stat(allowed_parent_dir.value().c_str(), &tmp_stat_info) != 0) { + PLOG(ERROR) << "Sandbox overly restrictive: unable to stat (" + << allowed_parent_dir.value() + << ")"; + return -1; + } + } + + // Test that we can't stat files outside the "allowed" directory. + { + struct stat tmp_stat_info; + if (stat(denied_file1.value().c_str(), &tmp_stat_info) > 0) { + PLOG(ERROR) << "Sandbox breach: was able to stat (" + << denied_file1.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() @@ -250,4 +301,4 @@ MULTIPROCESS_TEST_MAIN(mac_sandbox_path_access) { return 0; } -} // namespace +} // namespace sandbox |
