diff options
author | jam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-11 21:56:11 +0000 |
---|---|---|
committer | jam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-11 21:56:11 +0000 |
commit | 415c2cdea5dfa8bc87bd36a91bdb8473026f943f (patch) | |
tree | 22aefe975f1be77a1bb18cdd96aeb86fbd96e096 /content/common/sandbox_mac_diraccess_unittest.mm | |
parent | 5471bc3e9d08d1d955c96a8193ee2d9638c1591a (diff) | |
download | chromium_src-415c2cdea5dfa8bc87bd36a91bdb8473026f943f.zip chromium_src-415c2cdea5dfa8bc87bd36a91bdb8473026f943f.tar.gz chromium_src-415c2cdea5dfa8bc87bd36a91bdb8473026f943f.tar.bz2 |
Move some chrome\common code to content in preparation for moving chrome\gpu.
TBR=avi
Review URL: http://codereview.chromium.org/6686002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@77868 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/common/sandbox_mac_diraccess_unittest.mm')
-rw-r--r-- | content/common/sandbox_mac_diraccess_unittest.mm | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/content/common/sandbox_mac_diraccess_unittest.mm b/content/common/sandbox_mac_diraccess_unittest.mm new file mode 100644 index 0000000..8aad63b --- /dev/null +++ b/content/common/sandbox_mac_diraccess_unittest.mm @@ -0,0 +1,304 @@ +// Copyright (c) 2011 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/test/multiprocess_test.h" +#include "base/sys_string_conversions.h" +#include "base/utf_string_conversions.h" +#include "content/common/sandbox_mac.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/multiprocess_func_list.h" + +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: + bool CheckSandbox(const std::string& directory_to_try) { + setenv(kSandboxAccessPathKey, directory_to_try.c_str(), 1); + base::ProcessHandle child_process = SpawnChild("mac_sandbox_path_access", + false); + int code = -1; + if (!base::WaitForExitCode(child_process, &code)) { + LOG(WARNING) << "base::WaitForExitCode failed"; + return false; + } + return code == 0; + } +}; + +TEST_F(MacDirAccessSandboxTest, StringEscape) { + 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(Sandbox::QuotePlainString(in, &out)); + EXPECT_EQ(string_escape_cases[i].escaped, out); + } +} + +TEST_F(MacDirAccessSandboxTest, RegexEscape) { + 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(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(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(Sandbox::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(Sandbox::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(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); + 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(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())); + } +} + +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 a single directory. + NSString *sandbox_profile = + @"(version 1)" \ + "(deny default)" \ + "(allow signal (target self))" \ + "(allow sysctl-read)" \ + ";ENABLE_DIRECTORY_ACCESS"; + + std::string allowed_dir(sandbox_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; + } + + // Enable Sandbox. + char* error_buff = NULL; + 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; + } + 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(); + FilePath allowed_parent_dir = allowed_dir_path.DirName(); + std::string tricky_filename = basename.value() + "123"; + 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 (" + << allowed_file.value() + << ")"; + 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() + << ")"; + 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; +} + +} // namespace sandbox |