1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "sandbox/mac/bootstrap_sandbox.h"
7 #include <CoreFoundation/CoreFoundation.h>
8 #import <Foundation/Foundation.h>
10 #include <servers/bootstrap.h>
12 #include "base/logging.h"
13 #include "base/mac/mac_util.h"
14 #include "base/mac/mach_logging.h"
15 #include "base/mac/scoped_mach_port.h"
16 #include "base/mac/scoped_nsobject.h"
17 #include "base/process/kill.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/test/multiprocess_test.h"
20 #include "base/test/test_timeouts.h"
21 #include "sandbox/mac/pre_exec_delegate.h"
22 #include "sandbox/mac/xpc.h"
23 #import "testing/gtest_mac.h"
24 #include "testing/multiprocess_func_list.h"
26 NSString* const kTestNotification = @"org.chromium.bootstrap_sandbox_test";
28 @interface DistributedNotificationObserver : NSObject {
31 base::scoped_nsobject<NSString> object_;
35 - (void)waitForNotification;
38 @implementation DistributedNotificationObserver
40 if ((self = [super init])) {
41 [[NSDistributedNotificationCenter defaultCenter]
43 selector:@selector(observeNotification:)
44 name:kTestNotification
51 [[NSDistributedNotificationCenter defaultCenter]
53 name:kTestNotification
58 - (int)receivedCount {
59 return receivedCount_;
66 - (void)waitForNotification {
68 CFRunLoopRunInMode(kCFRunLoopDefaultMode,
69 TestTimeouts::action_timeout().InSeconds(), false);
72 - (void)observeNotification:(NSNotification*)notification {
74 object_.reset([[notification object] copy]);
75 CFRunLoopStop(CFRunLoopGetCurrent());
79 ////////////////////////////////////////////////////////////////////////////////
83 void InitializeXPCIfRequired() {
84 if (base::mac::IsOSYosemiteOrLater())
85 CHECK(InitializeXPC());
88 class BootstrapSandboxTest : public base::MultiProcessTest {
90 void SetUp() override {
91 base::MultiProcessTest::SetUp();
93 sandbox_ = BootstrapSandbox::Create();
94 ASSERT_TRUE(sandbox_.get());
97 BootstrapSandboxPolicy BaselinePolicy() {
98 BootstrapSandboxPolicy policy;
99 policy.rules["com.apple.cfprefsd.daemon"] = Rule(POLICY_ALLOW);
100 if (base::mac::IsOSSnowLeopard())
101 policy.rules["com.apple.SecurityServer"] = Rule(POLICY_ALLOW);
105 void RunChildWithPolicy(int policy_id,
106 const char* child_name,
107 base::ProcessHandle* out_pid) {
108 scoped_ptr<PreExecDelegate> pre_exec_delegate(
109 sandbox_->NewClient(policy_id));
111 base::LaunchOptions options;
112 options.pre_exec_delegate = pre_exec_delegate.get();
114 base::Process process = SpawnChildWithOptions(child_name, options);
115 ASSERT_TRUE(process.IsValid());
117 EXPECT_TRUE(process.WaitForExit(&code));
120 *out_pid = process.Pid();
124 scoped_ptr<BootstrapSandbox> sandbox_;
127 const char kNotificationTestMain[] = "PostNotification";
129 // Run the test without the sandbox.
130 TEST_F(BootstrapSandboxTest, DistributedNotifications_Unsandboxed) {
131 base::scoped_nsobject<DistributedNotificationObserver> observer(
132 [[DistributedNotificationObserver alloc] init]);
134 base::Process process = SpawnChild(kNotificationTestMain);
135 ASSERT_TRUE(process.IsValid());
137 EXPECT_TRUE(process.WaitForExit(&code));
140 [observer waitForNotification];
141 EXPECT_EQ(1, [observer receivedCount]);
142 EXPECT_EQ(process.Pid(), [[observer object] intValue]);
145 // Run the test with the sandbox enabled without notifications on the policy
147 TEST_F(BootstrapSandboxTest, DistributedNotifications_SandboxDeny) {
148 base::scoped_nsobject<DistributedNotificationObserver> observer(
149 [[DistributedNotificationObserver alloc] init]);
151 sandbox_->RegisterSandboxPolicy(1, BaselinePolicy());
152 RunChildWithPolicy(1, kNotificationTestMain, NULL);
154 [observer waitForNotification];
155 EXPECT_EQ(0, [observer receivedCount]);
156 EXPECT_EQ(nil, [observer object]);
159 // Run the test with notifications permitted.
160 TEST_F(BootstrapSandboxTest, DistributedNotifications_SandboxAllow) {
161 base::scoped_nsobject<DistributedNotificationObserver> observer(
162 [[DistributedNotificationObserver alloc] init]);
164 BootstrapSandboxPolicy policy(BaselinePolicy());
166 policy.rules["com.apple.distributed_notifications@Uv3"] = Rule(POLICY_ALLOW);
167 policy.rules["com.apple.distributed_notifications@1v3"] = Rule(POLICY_ALLOW);
169 policy.rules["com.apple.system.notification_center"] = Rule(POLICY_ALLOW);
170 policy.rules["com.apple.distributed_notifications.2"] = Rule(POLICY_ALLOW);
171 sandbox_->RegisterSandboxPolicy(2, policy);
173 base::ProcessHandle pid;
174 RunChildWithPolicy(2, kNotificationTestMain, &pid);
176 [observer waitForNotification];
177 EXPECT_EQ(1, [observer receivedCount]);
178 EXPECT_EQ(pid, [[observer object] intValue]);
181 MULTIPROCESS_TEST_MAIN(PostNotification) {
182 InitializeXPCIfRequired();
184 [[NSDistributedNotificationCenter defaultCenter]
185 postNotificationName:kTestNotification
186 object:[NSString stringWithFormat:@"%d", getpid()]];
190 const char kTestServer[] = "org.chromium.test_bootstrap_server";
192 TEST_F(BootstrapSandboxTest, PolicyDenyError) {
193 BootstrapSandboxPolicy policy(BaselinePolicy());
194 policy.rules[kTestServer] = Rule(POLICY_DENY_ERROR);
195 sandbox_->RegisterSandboxPolicy(1, policy);
197 RunChildWithPolicy(1, "PolicyDenyError", NULL);
200 MULTIPROCESS_TEST_MAIN(PolicyDenyError) {
201 InitializeXPCIfRequired();
203 mach_port_t port = MACH_PORT_NULL;
204 kern_return_t kr = bootstrap_look_up(bootstrap_port, kTestServer,
206 CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, kr);
207 CHECK(port == MACH_PORT_NULL);
209 kr = bootstrap_look_up(bootstrap_port, "org.chromium.some_other_server",
211 CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, kr);
212 CHECK(port == MACH_PORT_NULL);
217 TEST_F(BootstrapSandboxTest, PolicyDenyDummyPort) {
218 BootstrapSandboxPolicy policy(BaselinePolicy());
219 policy.rules[kTestServer] = Rule(POLICY_DENY_DUMMY_PORT);
220 sandbox_->RegisterSandboxPolicy(1, policy);
222 RunChildWithPolicy(1, "PolicyDenyDummyPort", NULL);
225 MULTIPROCESS_TEST_MAIN(PolicyDenyDummyPort) {
226 InitializeXPCIfRequired();
228 mach_port_t port = MACH_PORT_NULL;
229 kern_return_t kr = bootstrap_look_up(bootstrap_port, kTestServer,
231 CHECK_EQ(KERN_SUCCESS, kr);
232 CHECK(port != MACH_PORT_NULL);
236 struct SubstitutePortAckSend {
237 mach_msg_header_t header;
241 struct SubstitutePortAckRecv : public SubstitutePortAckSend {
242 mach_msg_trailer_t trailer;
245 const char kSubstituteAck[] = "Hello, this is doge!";
247 TEST_F(BootstrapSandboxTest, PolicySubstitutePort) {
248 mach_port_t task = mach_task_self();
251 ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
253 base::mac::ScopedMachReceiveRight scoped_port(port);
255 mach_port_urefs_t send_rights = 0;
256 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
258 EXPECT_EQ(0u, send_rights);
260 ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port,
261 MACH_MSG_TYPE_MAKE_SEND));
262 base::mac::ScopedMachSendRight scoped_port_send(port);
265 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
267 EXPECT_EQ(1u, send_rights);
269 BootstrapSandboxPolicy policy(BaselinePolicy());
270 policy.rules[kTestServer] = Rule(port);
271 sandbox_->RegisterSandboxPolicy(1, policy);
273 RunChildWithPolicy(1, "PolicySubstitutePort", NULL);
275 struct SubstitutePortAckRecv msg;
276 bzero(&msg, sizeof(msg));
277 msg.header.msgh_size = sizeof(msg);
278 msg.header.msgh_local_port = port;
279 kern_return_t kr = mach_msg(&msg.header, MACH_RCV_MSG, 0,
280 msg.header.msgh_size, port,
281 TestTimeouts::tiny_timeout().InMilliseconds(), MACH_PORT_NULL);
282 EXPECT_EQ(KERN_SUCCESS, kr);
285 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
287 EXPECT_EQ(1u, send_rights);
289 EXPECT_EQ(0, strncmp(kSubstituteAck, msg.buf, sizeof(msg.buf)));
292 MULTIPROCESS_TEST_MAIN(PolicySubstitutePort) {
293 InitializeXPCIfRequired();
295 mach_port_t port = MACH_PORT_NULL;
296 kern_return_t kr = bootstrap_look_up(bootstrap_port, kTestServer, &port);
297 CHECK_EQ(KERN_SUCCESS, kr);
298 CHECK(port != MACH_PORT_NULL);
300 struct SubstitutePortAckSend msg;
301 bzero(&msg, sizeof(msg));
302 msg.header.msgh_size = sizeof(msg);
303 msg.header.msgh_remote_port = port;
304 msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND);
305 strncpy(msg.buf, kSubstituteAck, sizeof(msg.buf));
307 CHECK_EQ(KERN_SUCCESS, mach_msg_send(&msg.header));
312 TEST_F(BootstrapSandboxTest, ForwardMessageInProcess) {
313 mach_port_t task = mach_task_self();
316 ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
318 base::mac::ScopedMachReceiveRight scoped_port_recv(port);
320 mach_port_urefs_t send_rights = 0;
321 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
323 EXPECT_EQ(0u, send_rights);
325 ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port,
326 MACH_MSG_TYPE_MAKE_SEND));
327 base::mac::ScopedMachSendRight scoped_port_send(port);
330 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
332 EXPECT_EQ(1u, send_rights);
335 ASSERT_EQ(KERN_SUCCESS, task_get_bootstrap_port(task, &bp));
336 base::mac::ScopedMachSendRight scoped_bp(bp);
338 char service_name[] = "org.chromium.sandbox.test.ForwardMessageInProcess";
339 #pragma GCC diagnostic push
340 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
341 kern_return_t kr = bootstrap_register(bp, service_name, port);
342 #pragma GCC diagnostic pop
343 EXPECT_EQ(KERN_SUCCESS, kr);
346 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
348 EXPECT_EQ(1u, send_rights);
350 mach_port_t service_port;
351 EXPECT_EQ(KERN_SUCCESS, bootstrap_look_up(bp, service_name, &service_port));
352 base::mac::ScopedMachSendRight scoped_service_port(service_port);
355 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
357 // On 10.6, bootstrap_lookup2 may add an extra right to place it in a per-
359 if (base::mac::IsOSSnowLeopard())
360 EXPECT_TRUE(send_rights == 3u || send_rights == 2u) << send_rights;
362 EXPECT_EQ(2u, send_rights);
365 const char kDefaultRuleTestAllow[] =
366 "org.chromium.sandbox.test.DefaultRuleAllow";
367 const char kDefaultRuleTestDeny[] =
368 "org.chromium.sandbox.test.DefaultRuleAllow.Deny";
370 TEST_F(BootstrapSandboxTest, DefaultRuleAllow) {
371 mach_port_t task = mach_task_self();
374 ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
376 base::mac::ScopedMachReceiveRight scoped_port_recv(port);
378 ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port,
379 MACH_MSG_TYPE_MAKE_SEND));
380 base::mac::ScopedMachSendRight scoped_port_send(port);
382 BootstrapSandboxPolicy policy;
383 policy.default_rule = Rule(POLICY_ALLOW);
384 policy.rules[kDefaultRuleTestAllow] = Rule(port);
385 policy.rules[kDefaultRuleTestDeny] = Rule(POLICY_DENY_ERROR);
386 sandbox_->RegisterSandboxPolicy(3, policy);
388 base::scoped_nsobject<DistributedNotificationObserver> observer(
389 [[DistributedNotificationObserver alloc] init]);
392 RunChildWithPolicy(3, "DefaultRuleAllow", &pid);
395 [observer waitForNotification];
396 EXPECT_EQ(1, [observer receivedCount]);
397 EXPECT_EQ(pid, [[observer object] intValue]);
399 struct SubstitutePortAckRecv msg;
400 bzero(&msg, sizeof(msg));
401 msg.header.msgh_size = sizeof(msg);
402 msg.header.msgh_local_port = port;
403 kern_return_t kr = mach_msg(&msg.header, MACH_RCV_MSG, 0,
404 msg.header.msgh_size, port,
405 TestTimeouts::tiny_timeout().InMilliseconds(), MACH_PORT_NULL);
406 EXPECT_EQ(KERN_SUCCESS, kr);
408 EXPECT_EQ(0, strncmp(kSubstituteAck, msg.buf, sizeof(msg.buf)));
411 MULTIPROCESS_TEST_MAIN(DefaultRuleAllow) {
412 InitializeXPCIfRequired();
414 [[NSDistributedNotificationCenter defaultCenter]
415 postNotificationName:kTestNotification
416 object:[NSString stringWithFormat:@"%d", getpid()]];
418 mach_port_t port = MACH_PORT_NULL;
419 CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, bootstrap_look_up(bootstrap_port,
420 const_cast<char*>(kDefaultRuleTestDeny), &port));
421 CHECK(port == MACH_PORT_NULL);
423 CHECK_EQ(KERN_SUCCESS, bootstrap_look_up(bootstrap_port,
424 const_cast<char*>(kDefaultRuleTestAllow), &port));
425 CHECK(port != MACH_PORT_NULL);
427 struct SubstitutePortAckSend msg;
428 bzero(&msg, sizeof(msg));
429 msg.header.msgh_size = sizeof(msg);
430 msg.header.msgh_remote_port = port;
431 msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND);
432 strncpy(msg.buf, kSubstituteAck, sizeof(msg.buf));
434 CHECK_EQ(KERN_SUCCESS, mach_msg_send(&msg.header));
439 TEST_F(BootstrapSandboxTest, ChildOutliveSandbox) {
440 const int kTestPolicyId = 1;
441 mach_port_t task = mach_task_self();
443 // Create a server port.
445 ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
447 base::mac::ScopedMachReceiveRight scoped_port_recv(port);
449 ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port,
450 MACH_MSG_TYPE_MAKE_SEND));
451 base::mac::ScopedMachSendRight scoped_port_send(port);
453 // Set up the policy and register the port.
454 BootstrapSandboxPolicy policy(BaselinePolicy());
455 policy.rules["sync"] = Rule(port);
456 sandbox_->RegisterSandboxPolicy(kTestPolicyId, policy);
459 scoped_ptr<PreExecDelegate> pre_exec_delegate(
460 sandbox_->NewClient(kTestPolicyId));
461 base::LaunchOptions options;
462 options.pre_exec_delegate = pre_exec_delegate.get();
463 base::Process process = SpawnChildWithOptions("ChildOutliveSandbox", options);
464 ASSERT_TRUE(process.IsValid());
466 // Synchronize with the child.
467 mach_msg_empty_rcv_t rcv_msg;
468 bzero(&rcv_msg, sizeof(rcv_msg));
469 kern_return_t kr = mach_msg(&rcv_msg.header, MACH_RCV_MSG, 0,
470 sizeof(rcv_msg), port,
471 TestTimeouts::tiny_timeout().InMilliseconds(), MACH_PORT_NULL);
472 ASSERT_EQ(KERN_SUCCESS, kr) << mach_error_string(kr);
474 // Destroy the sandbox.
477 // Synchronize again with the child.
478 mach_msg_empty_send_t send_msg;
479 bzero(&send_msg, sizeof(send_msg));
480 send_msg.header.msgh_size = sizeof(send_msg);
481 send_msg.header.msgh_remote_port = rcv_msg.header.msgh_remote_port;
482 send_msg.header.msgh_bits =
483 MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND_ONCE);
484 kr = mach_msg(&send_msg.header, MACH_SEND_MSG, send_msg.header.msgh_size, 0,
485 MACH_PORT_NULL, TestTimeouts::tiny_timeout().InMilliseconds(),
487 EXPECT_EQ(KERN_SUCCESS, kr) << mach_error_string(kr);
490 EXPECT_TRUE(process.WaitForExit(&code));
494 MULTIPROCESS_TEST_MAIN(ChildOutliveSandbox) {
495 InitializeXPCIfRequired();
497 // Get the synchronization channel.
498 mach_port_t port = MACH_PORT_NULL;
499 CHECK_EQ(KERN_SUCCESS, bootstrap_look_up(bootstrap_port, "sync", &port));
501 // Create a reply port.
502 mach_port_t reply_port;
503 CHECK_EQ(KERN_SUCCESS, mach_port_allocate(mach_task_self(),
504 MACH_PORT_RIGHT_RECEIVE, &reply_port));
505 base::mac::ScopedMachReceiveRight scoped_reply_port(reply_port);
507 // Send a message to shutdown the sandbox.
508 mach_msg_empty_send_t send_msg;
509 bzero(&send_msg, sizeof(send_msg));
510 send_msg.header.msgh_size = sizeof(send_msg);
511 send_msg.header.msgh_local_port = reply_port;
512 send_msg.header.msgh_remote_port = port;
513 send_msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND,
514 MACH_MSG_TYPE_MAKE_SEND_ONCE);
515 kern_return_t kr = mach_msg_send(&send_msg.header);
516 MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_msg_send";
518 // Flood the server's message queue with messages. There should be some
519 // pending when the sandbox is destroyed.
520 for (int i = 0; i < 20; ++i) {
521 mach_port_t tmp = MACH_PORT_NULL;
522 std::string name = base::StringPrintf("test.%d", i);
523 bootstrap_look_up(bootstrap_port, const_cast<char*>(name.c_str()), &tmp);
526 // Ack that the sandbox has been shutdown.
527 mach_msg_empty_rcv_t rcv_msg;
528 bzero(&rcv_msg, sizeof(rcv_msg));
529 rcv_msg.header.msgh_size = sizeof(rcv_msg);
530 rcv_msg.header.msgh_local_port = reply_port;
531 kr = mach_msg_receive(&rcv_msg.header);
532 MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_msg_receive";
534 // Try to message the sandbox.
535 bootstrap_look_up(bootstrap_port, "test", &port);
540 } // namespace sandbox