diff options
Diffstat (limited to 'chromium/sandbox/mac/bootstrap_sandbox_unittest.mm')
-rw-r--r-- | chromium/sandbox/mac/bootstrap_sandbox_unittest.mm | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/chromium/sandbox/mac/bootstrap_sandbox_unittest.mm b/chromium/sandbox/mac/bootstrap_sandbox_unittest.mm new file mode 100644 index 00000000000..85f627f10a3 --- /dev/null +++ b/chromium/sandbox/mac/bootstrap_sandbox_unittest.mm @@ -0,0 +1,417 @@ +// Copyright 2014 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. + +#include "sandbox/mac/bootstrap_sandbox.h" + +#include <CoreFoundation/CoreFoundation.h> +#import <Foundation/Foundation.h> +#include <mach/mach.h> +#include <servers/bootstrap.h> + +#include "base/logging.h" +#include "base/mac/mac_util.h" +#include "base/mac/scoped_nsobject.h" +#include "base/mac/scoped_mach_port.h" +#include "base/process/kill.h" +#include "base/test/multiprocess_test.h" +#include "base/test/test_timeouts.h" +#import "testing/gtest_mac.h" +#include "testing/multiprocess_func_list.h" + +NSString* const kTestNotification = @"org.chromium.bootstrap_sandbox_test"; + +@interface DistributedNotificationObserver : NSObject { + @private + int receivedCount_; + base::scoped_nsobject<NSString> object_; +} +- (int)receivedCount; +- (NSString*)object; +- (void)waitForNotification; +@end + +@implementation DistributedNotificationObserver +- (id)init { + if ((self = [super init])) { + [[NSDistributedNotificationCenter defaultCenter] + addObserver:self + selector:@selector(observeNotification:) + name:kTestNotification + object:nil]; + } + return self; +} + +- (void)dealloc { + [[NSDistributedNotificationCenter defaultCenter] + removeObserver:self + name:kTestNotification + object:nil]; + [super dealloc]; +} + +- (int)receivedCount { + return receivedCount_; +} + +- (NSString*)object { + return object_.get(); +} + +- (void)waitForNotification { + object_.reset(); + CFRunLoopRunInMode(kCFRunLoopDefaultMode, + TestTimeouts::action_timeout().InSeconds(), false); +} + +- (void)observeNotification:(NSNotification*)notification { + ++receivedCount_; + object_.reset([[notification object] copy]); + CFRunLoopStop(CFRunLoopGetCurrent()); +} +@end + +//////////////////////////////////////////////////////////////////////////////// + +namespace sandbox { + +class BootstrapSandboxTest : public base::MultiProcessTest { + public: + virtual void SetUp() OVERRIDE { + base::MultiProcessTest::SetUp(); + + sandbox_ = BootstrapSandbox::Create(); + ASSERT_TRUE(sandbox_.get()); + } + + BootstrapSandboxPolicy BaselinePolicy() { + BootstrapSandboxPolicy policy; + if (base::mac::IsOSSnowLeopard()) + policy.rules["com.apple.SecurityServer"] = Rule(POLICY_ALLOW); + return policy; + } + + void RunChildWithPolicy(int policy_id, + const char* child_name, + base::ProcessHandle* out_pid) { + sandbox_->PrepareToForkWithPolicy(policy_id); + base::LaunchOptions options; + options.replacement_bootstrap_name = sandbox_->server_bootstrap_name(); + base::ProcessHandle pid = SpawnChildWithOptions(child_name, options); + ASSERT_GT(pid, 0); + sandbox_->FinishedFork(pid); + int code = 0; + EXPECT_TRUE(base::WaitForExitCode(pid, &code)); + EXPECT_EQ(0, code); + if (out_pid) + *out_pid = pid; + } + + protected: + scoped_ptr<BootstrapSandbox> sandbox_; +}; + +const char kNotificationTestMain[] = "PostNotification"; + +// Run the test without the sandbox. +TEST_F(BootstrapSandboxTest, DistributedNotifications_Unsandboxed) { + base::scoped_nsobject<DistributedNotificationObserver> observer( + [[DistributedNotificationObserver alloc] init]); + + base::ProcessHandle pid = SpawnChild(kNotificationTestMain); + ASSERT_GT(pid, 0); + int code = 0; + EXPECT_TRUE(base::WaitForExitCode(pid, &code)); + EXPECT_EQ(0, code); + + [observer waitForNotification]; + EXPECT_EQ(1, [observer receivedCount]); + EXPECT_EQ(pid, [[observer object] intValue]); +} + +// Run the test with the sandbox enabled without notifications on the policy +// whitelist. +TEST_F(BootstrapSandboxTest, DistributedNotifications_SandboxDeny) { + base::scoped_nsobject<DistributedNotificationObserver> observer( + [[DistributedNotificationObserver alloc] init]); + + sandbox_->RegisterSandboxPolicy(1, BaselinePolicy()); + RunChildWithPolicy(1, kNotificationTestMain, NULL); + + [observer waitForNotification]; + EXPECT_EQ(0, [observer receivedCount]); + EXPECT_EQ(nil, [observer object]); +} + +// Run the test with notifications permitted. +TEST_F(BootstrapSandboxTest, DistributedNotifications_SandboxAllow) { + base::scoped_nsobject<DistributedNotificationObserver> observer( + [[DistributedNotificationObserver alloc] init]); + + BootstrapSandboxPolicy policy(BaselinePolicy()); + // 10.9: + policy.rules["com.apple.distributed_notifications@Uv3"] = Rule(POLICY_ALLOW); + policy.rules["com.apple.distributed_notifications@1v3"] = Rule(POLICY_ALLOW); + // 10.6: + policy.rules["com.apple.system.notification_center"] = Rule(POLICY_ALLOW); + policy.rules["com.apple.distributed_notifications.2"] = Rule(POLICY_ALLOW); + sandbox_->RegisterSandboxPolicy(2, policy); + + base::ProcessHandle pid; + RunChildWithPolicy(2, kNotificationTestMain, &pid); + + [observer waitForNotification]; + EXPECT_EQ(1, [observer receivedCount]); + EXPECT_EQ(pid, [[observer object] intValue]); +} + +MULTIPROCESS_TEST_MAIN(PostNotification) { + [[NSDistributedNotificationCenter defaultCenter] + postNotificationName:kTestNotification + object:[NSString stringWithFormat:@"%d", getpid()]]; + return 0; +} + +const char kTestServer[] = "org.chromium.test_bootstrap_server"; + +TEST_F(BootstrapSandboxTest, PolicyDenyError) { + BootstrapSandboxPolicy policy(BaselinePolicy()); + policy.rules[kTestServer] = Rule(POLICY_DENY_ERROR); + sandbox_->RegisterSandboxPolicy(1, policy); + + RunChildWithPolicy(1, "PolicyDenyError", NULL); +} + +MULTIPROCESS_TEST_MAIN(PolicyDenyError) { + mach_port_t port = MACH_PORT_NULL; + kern_return_t kr = bootstrap_look_up(bootstrap_port, kTestServer, + &port); + CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, kr); + CHECK(port == MACH_PORT_NULL); + + kr = bootstrap_look_up(bootstrap_port, "org.chromium.some_other_server", + &port); + CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, kr); + CHECK(port == MACH_PORT_NULL); + + return 0; +} + +TEST_F(BootstrapSandboxTest, PolicyDenyDummyPort) { + BootstrapSandboxPolicy policy(BaselinePolicy()); + policy.rules[kTestServer] = Rule(POLICY_DENY_DUMMY_PORT); + sandbox_->RegisterSandboxPolicy(1, policy); + + RunChildWithPolicy(1, "PolicyDenyDummyPort", NULL); +} + +MULTIPROCESS_TEST_MAIN(PolicyDenyDummyPort) { + mach_port_t port = MACH_PORT_NULL; + kern_return_t kr = bootstrap_look_up(bootstrap_port, kTestServer, + &port); + CHECK_EQ(KERN_SUCCESS, kr); + CHECK(port != MACH_PORT_NULL); + return 0; +} + +struct SubstitutePortAckSend { + mach_msg_header_t header; + char buf[32]; +}; + +struct SubstitutePortAckRecv : public SubstitutePortAckSend { + mach_msg_trailer_t trailer; +}; + +const char kSubstituteAck[] = "Hello, this is doge!"; + +TEST_F(BootstrapSandboxTest, PolicySubstitutePort) { + mach_port_t task = mach_task_self(); + + mach_port_t port; + ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, + &port)); + base::mac::ScopedMachReceiveRight scoped_port(port); + + mach_port_urefs_t send_rights = 0; + ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND, + &send_rights)); + EXPECT_EQ(0u, send_rights); + + ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port, + MACH_MSG_TYPE_MAKE_SEND)); + base::mac::ScopedMachSendRight scoped_port_send(port); + + send_rights = 0; + ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND, + &send_rights)); + EXPECT_EQ(1u, send_rights); + + BootstrapSandboxPolicy policy(BaselinePolicy()); + policy.rules[kTestServer] = Rule(port); + sandbox_->RegisterSandboxPolicy(1, policy); + + RunChildWithPolicy(1, "PolicySubstitutePort", NULL); + + struct SubstitutePortAckRecv msg; + bzero(&msg, sizeof(msg)); + msg.header.msgh_size = sizeof(msg); + msg.header.msgh_local_port = port; + kern_return_t kr = mach_msg(&msg.header, MACH_RCV_MSG, 0, + msg.header.msgh_size, port, + TestTimeouts::tiny_timeout().InMilliseconds(), MACH_PORT_NULL); + EXPECT_EQ(KERN_SUCCESS, kr); + + send_rights = 0; + ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND, + &send_rights)); + EXPECT_EQ(1u, send_rights); + + EXPECT_EQ(0, strncmp(kSubstituteAck, msg.buf, sizeof(msg.buf))); +} + +MULTIPROCESS_TEST_MAIN(PolicySubstitutePort) { + mach_port_t port = MACH_PORT_NULL; + kern_return_t kr = bootstrap_look_up(bootstrap_port, kTestServer, &port); + CHECK_EQ(KERN_SUCCESS, kr); + CHECK(port != MACH_PORT_NULL); + + struct SubstitutePortAckSend msg; + bzero(&msg, sizeof(msg)); + msg.header.msgh_size = sizeof(msg); + msg.header.msgh_remote_port = port; + msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND); + strncpy(msg.buf, kSubstituteAck, sizeof(msg.buf)); + + CHECK_EQ(KERN_SUCCESS, mach_msg_send(&msg.header)); + + return 0; +} + +TEST_F(BootstrapSandboxTest, ForwardMessageInProcess) { + mach_port_t task = mach_task_self(); + + mach_port_t port; + ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, + &port)); + base::mac::ScopedMachReceiveRight scoped_port_recv(port); + + mach_port_urefs_t send_rights = 0; + ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND, + &send_rights)); + EXPECT_EQ(0u, send_rights); + + ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port, + MACH_MSG_TYPE_MAKE_SEND)); + base::mac::ScopedMachSendRight scoped_port_send(port); + + send_rights = 0; + ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND, + &send_rights)); + EXPECT_EQ(1u, send_rights); + + mach_port_t bp; + ASSERT_EQ(KERN_SUCCESS, task_get_bootstrap_port(task, &bp)); + base::mac::ScopedMachSendRight scoped_bp(bp); + + char service_name[] = "org.chromium.sandbox.test.ForwardMessageInProcess"; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + kern_return_t kr = bootstrap_register(bp, service_name, port); +#pragma GCC diagnostic pop + EXPECT_EQ(KERN_SUCCESS, kr); + + send_rights = 0; + ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND, + &send_rights)); + EXPECT_EQ(1u, send_rights); + + mach_port_t service_port; + EXPECT_EQ(KERN_SUCCESS, bootstrap_look_up(bp, service_name, &service_port)); + base::mac::ScopedMachSendRight scoped_service_port(service_port); + + send_rights = 0; + ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND, + &send_rights)); + // On 10.6, bootstrap_lookup2 may add an extra right to place it in a per- + // process cache. + if (base::mac::IsOSSnowLeopard()) + EXPECT_TRUE(send_rights == 3u || send_rights == 2u) << send_rights; + else + EXPECT_EQ(2u, send_rights); +} + +const char kDefaultRuleTestAllow[] = + "org.chromium.sandbox.test.DefaultRuleAllow"; +const char kDefaultRuleTestDeny[] = + "org.chromium.sandbox.test.DefaultRuleAllow.Deny"; + +TEST_F(BootstrapSandboxTest, DefaultRuleAllow) { + mach_port_t task = mach_task_self(); + + mach_port_t port; + ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, + &port)); + base::mac::ScopedMachReceiveRight scoped_port_recv(port); + + ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port, + MACH_MSG_TYPE_MAKE_SEND)); + base::mac::ScopedMachSendRight scoped_port_send(port); + + BootstrapSandboxPolicy policy; + policy.default_rule = Rule(POLICY_ALLOW); + policy.rules[kDefaultRuleTestAllow] = Rule(port); + policy.rules[kDefaultRuleTestDeny] = Rule(POLICY_DENY_ERROR); + sandbox_->RegisterSandboxPolicy(3, policy); + + base::scoped_nsobject<DistributedNotificationObserver> observer( + [[DistributedNotificationObserver alloc] init]); + + int pid = 0; + RunChildWithPolicy(3, "DefaultRuleAllow", &pid); + EXPECT_GT(pid, 0); + + [observer waitForNotification]; + EXPECT_EQ(1, [observer receivedCount]); + EXPECT_EQ(pid, [[observer object] intValue]); + + struct SubstitutePortAckRecv msg; + bzero(&msg, sizeof(msg)); + msg.header.msgh_size = sizeof(msg); + msg.header.msgh_local_port = port; + kern_return_t kr = mach_msg(&msg.header, MACH_RCV_MSG, 0, + msg.header.msgh_size, port, + TestTimeouts::tiny_timeout().InMilliseconds(), MACH_PORT_NULL); + EXPECT_EQ(KERN_SUCCESS, kr); + + EXPECT_EQ(0, strncmp(kSubstituteAck, msg.buf, sizeof(msg.buf))); +} + +MULTIPROCESS_TEST_MAIN(DefaultRuleAllow) { + [[NSDistributedNotificationCenter defaultCenter] + postNotificationName:kTestNotification + object:[NSString stringWithFormat:@"%d", getpid()]]; + + mach_port_t port = MACH_PORT_NULL; + CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, bootstrap_look_up(bootstrap_port, + const_cast<char*>(kDefaultRuleTestDeny), &port)); + CHECK(port == MACH_PORT_NULL); + + CHECK_EQ(KERN_SUCCESS, bootstrap_look_up(bootstrap_port, + const_cast<char*>(kDefaultRuleTestAllow), &port)); + CHECK(port != MACH_PORT_NULL); + + struct SubstitutePortAckSend msg; + bzero(&msg, sizeof(msg)); + msg.header.msgh_size = sizeof(msg); + msg.header.msgh_remote_port = port; + msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND); + strncpy(msg.buf, kSubstituteAck, sizeof(msg.buf)); + + CHECK_EQ(KERN_SUCCESS, mach_msg_send(&msg.header)); + + return 0; +} + +} // namespace sandbox |