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/xpc.h"
22 #import "testing/gtest_mac.h"
23 #include "testing/multiprocess_func_list.h"
25 NSString* const kTestNotification = @"org.chromium.bootstrap_sandbox_test";
27 @interface DistributedNotificationObserver : NSObject {
30 base::scoped_nsobject<NSString> object_;
34 - (void)waitForNotification;
37 @implementation DistributedNotificationObserver
39 if ((self = [super init])) {
40 [[NSDistributedNotificationCenter defaultCenter]
42 selector:@selector(observeNotification:)
43 name:kTestNotification
50 [[NSDistributedNotificationCenter defaultCenter]
52 name:kTestNotification
57 - (int)receivedCount {
58 return receivedCount_;
65 - (void)waitForNotification {
67 CFRunLoopRunInMode(kCFRunLoopDefaultMode,
68 TestTimeouts::action_timeout().InSeconds(), false);
71 - (void)observeNotification:(NSNotification*)notification {
73 object_.reset([[notification object] copy]);
74 CFRunLoopStop(CFRunLoopGetCurrent());
78 ////////////////////////////////////////////////////////////////////////////////
82 void InitializeXPCIfRequired() {
83 if (base::mac::IsOSYosemiteOrLater())
84 CHECK(InitializeXPC());
87 class BootstrapSandboxTest : public base::MultiProcessTest {
89 void SetUp() override {
90 base::MultiProcessTest::SetUp();
92 sandbox_ = BootstrapSandbox::Create();
93 ASSERT_TRUE(sandbox_.get());
96 BootstrapSandboxPolicy BaselinePolicy() {
97 BootstrapSandboxPolicy policy;
98 policy.rules["com.apple.cfprefsd.daemon"] = Rule(POLICY_ALLOW);
99 if (base::mac::IsOSSnowLeopard())
100 policy.rules["com.apple.SecurityServer"] = Rule(POLICY_ALLOW);
104 void RunChildWithPolicy(int policy_id,
105 const char* child_name,
106 base::ProcessHandle* out_pid) {
107 sandbox_->PrepareToForkWithPolicy(policy_id);
108 base::LaunchOptions options;
109 options.replacement_bootstrap_name = sandbox_->server_bootstrap_name();
110 base::Process process = SpawnChildWithOptions(child_name, options);
111 ASSERT_TRUE(process.IsValid());
112 sandbox_->FinishedFork(process.Handle());
114 EXPECT_TRUE(process.WaitForExit(&code));
117 *out_pid = process.Pid();
121 scoped_ptr<BootstrapSandbox> sandbox_;
124 const char kNotificationTestMain[] = "PostNotification";
126 // Run the test without the sandbox.
127 TEST_F(BootstrapSandboxTest, DistributedNotifications_Unsandboxed) {
128 base::scoped_nsobject<DistributedNotificationObserver> observer(
129 [[DistributedNotificationObserver alloc] init]);
131 base::Process process = SpawnChild(kNotificationTestMain);
132 ASSERT_TRUE(process.IsValid());
134 EXPECT_TRUE(process.WaitForExit(&code));
137 [observer waitForNotification];
138 EXPECT_EQ(1, [observer receivedCount]);
139 EXPECT_EQ(process.Pid(), [[observer object] intValue]);
142 // Run the test with the sandbox enabled without notifications on the policy
144 TEST_F(BootstrapSandboxTest, DistributedNotifications_SandboxDeny) {
145 base::scoped_nsobject<DistributedNotificationObserver> observer(
146 [[DistributedNotificationObserver alloc] init]);
148 sandbox_->RegisterSandboxPolicy(1, BaselinePolicy());
149 RunChildWithPolicy(1, kNotificationTestMain, NULL);
151 [observer waitForNotification];
152 EXPECT_EQ(0, [observer receivedCount]);
153 EXPECT_EQ(nil, [observer object]);
156 // Run the test with notifications permitted.
157 TEST_F(BootstrapSandboxTest, DistributedNotifications_SandboxAllow) {
158 base::scoped_nsobject<DistributedNotificationObserver> observer(
159 [[DistributedNotificationObserver alloc] init]);
161 BootstrapSandboxPolicy policy(BaselinePolicy());
163 policy.rules["com.apple.distributed_notifications@Uv3"] = Rule(POLICY_ALLOW);
164 policy.rules["com.apple.distributed_notifications@1v3"] = Rule(POLICY_ALLOW);
166 policy.rules["com.apple.system.notification_center"] = Rule(POLICY_ALLOW);
167 policy.rules["com.apple.distributed_notifications.2"] = Rule(POLICY_ALLOW);
168 sandbox_->RegisterSandboxPolicy(2, policy);
170 base::ProcessHandle pid;
171 RunChildWithPolicy(2, kNotificationTestMain, &pid);
173 [observer waitForNotification];
174 EXPECT_EQ(1, [observer receivedCount]);
175 EXPECT_EQ(pid, [[observer object] intValue]);
178 MULTIPROCESS_TEST_MAIN(PostNotification) {
179 InitializeXPCIfRequired();
181 [[NSDistributedNotificationCenter defaultCenter]
182 postNotificationName:kTestNotification
183 object:[NSString stringWithFormat:@"%d", getpid()]];
187 const char kTestServer[] = "org.chromium.test_bootstrap_server";
189 TEST_F(BootstrapSandboxTest, PolicyDenyError) {
190 BootstrapSandboxPolicy policy(BaselinePolicy());
191 policy.rules[kTestServer] = Rule(POLICY_DENY_ERROR);
192 sandbox_->RegisterSandboxPolicy(1, policy);
194 RunChildWithPolicy(1, "PolicyDenyError", NULL);
197 MULTIPROCESS_TEST_MAIN(PolicyDenyError) {
198 InitializeXPCIfRequired();
200 mach_port_t port = MACH_PORT_NULL;
201 kern_return_t kr = bootstrap_look_up(bootstrap_port, kTestServer,
203 CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, kr);
204 CHECK(port == MACH_PORT_NULL);
206 kr = bootstrap_look_up(bootstrap_port, "org.chromium.some_other_server",
208 CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, kr);
209 CHECK(port == MACH_PORT_NULL);
214 TEST_F(BootstrapSandboxTest, PolicyDenyDummyPort) {
215 BootstrapSandboxPolicy policy(BaselinePolicy());
216 policy.rules[kTestServer] = Rule(POLICY_DENY_DUMMY_PORT);
217 sandbox_->RegisterSandboxPolicy(1, policy);
219 RunChildWithPolicy(1, "PolicyDenyDummyPort", NULL);
222 MULTIPROCESS_TEST_MAIN(PolicyDenyDummyPort) {
223 InitializeXPCIfRequired();
225 mach_port_t port = MACH_PORT_NULL;
226 kern_return_t kr = bootstrap_look_up(bootstrap_port, kTestServer,
228 CHECK_EQ(KERN_SUCCESS, kr);
229 CHECK(port != MACH_PORT_NULL);
233 struct SubstitutePortAckSend {
234 mach_msg_header_t header;
238 struct SubstitutePortAckRecv : public SubstitutePortAckSend {
239 mach_msg_trailer_t trailer;
242 const char kSubstituteAck[] = "Hello, this is doge!";
244 TEST_F(BootstrapSandboxTest, PolicySubstitutePort) {
245 mach_port_t task = mach_task_self();
248 ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
250 base::mac::ScopedMachReceiveRight scoped_port(port);
252 mach_port_urefs_t send_rights = 0;
253 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
255 EXPECT_EQ(0u, send_rights);
257 ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port,
258 MACH_MSG_TYPE_MAKE_SEND));
259 base::mac::ScopedMachSendRight scoped_port_send(port);
262 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
264 EXPECT_EQ(1u, send_rights);
266 BootstrapSandboxPolicy policy(BaselinePolicy());
267 policy.rules[kTestServer] = Rule(port);
268 sandbox_->RegisterSandboxPolicy(1, policy);
270 RunChildWithPolicy(1, "PolicySubstitutePort", NULL);
272 struct SubstitutePortAckRecv msg;
273 bzero(&msg, sizeof(msg));
274 msg.header.msgh_size = sizeof(msg);
275 msg.header.msgh_local_port = port;
276 kern_return_t kr = mach_msg(&msg.header, MACH_RCV_MSG, 0,
277 msg.header.msgh_size, port,
278 TestTimeouts::tiny_timeout().InMilliseconds(), MACH_PORT_NULL);
279 EXPECT_EQ(KERN_SUCCESS, kr);
282 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
284 EXPECT_EQ(1u, send_rights);
286 EXPECT_EQ(0, strncmp(kSubstituteAck, msg.buf, sizeof(msg.buf)));
289 MULTIPROCESS_TEST_MAIN(PolicySubstitutePort) {
290 InitializeXPCIfRequired();
292 mach_port_t port = MACH_PORT_NULL;
293 kern_return_t kr = bootstrap_look_up(bootstrap_port, kTestServer, &port);
294 CHECK_EQ(KERN_SUCCESS, kr);
295 CHECK(port != MACH_PORT_NULL);
297 struct SubstitutePortAckSend msg;
298 bzero(&msg, sizeof(msg));
299 msg.header.msgh_size = sizeof(msg);
300 msg.header.msgh_remote_port = port;
301 msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND);
302 strncpy(msg.buf, kSubstituteAck, sizeof(msg.buf));
304 CHECK_EQ(KERN_SUCCESS, mach_msg_send(&msg.header));
309 TEST_F(BootstrapSandboxTest, ForwardMessageInProcess) {
310 mach_port_t task = mach_task_self();
313 ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
315 base::mac::ScopedMachReceiveRight scoped_port_recv(port);
317 mach_port_urefs_t send_rights = 0;
318 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
320 EXPECT_EQ(0u, send_rights);
322 ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port,
323 MACH_MSG_TYPE_MAKE_SEND));
324 base::mac::ScopedMachSendRight scoped_port_send(port);
327 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
329 EXPECT_EQ(1u, send_rights);
332 ASSERT_EQ(KERN_SUCCESS, task_get_bootstrap_port(task, &bp));
333 base::mac::ScopedMachSendRight scoped_bp(bp);
335 char service_name[] = "org.chromium.sandbox.test.ForwardMessageInProcess";
336 #pragma GCC diagnostic push
337 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
338 kern_return_t kr = bootstrap_register(bp, service_name, port);
339 #pragma GCC diagnostic pop
340 EXPECT_EQ(KERN_SUCCESS, kr);
343 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
345 EXPECT_EQ(1u, send_rights);
347 mach_port_t service_port;
348 EXPECT_EQ(KERN_SUCCESS, bootstrap_look_up(bp, service_name, &service_port));
349 base::mac::ScopedMachSendRight scoped_service_port(service_port);
352 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
354 // On 10.6, bootstrap_lookup2 may add an extra right to place it in a per-
356 if (base::mac::IsOSSnowLeopard())
357 EXPECT_TRUE(send_rights == 3u || send_rights == 2u) << send_rights;
359 EXPECT_EQ(2u, send_rights);
362 const char kDefaultRuleTestAllow[] =
363 "org.chromium.sandbox.test.DefaultRuleAllow";
364 const char kDefaultRuleTestDeny[] =
365 "org.chromium.sandbox.test.DefaultRuleAllow.Deny";
367 TEST_F(BootstrapSandboxTest, DefaultRuleAllow) {
368 mach_port_t task = mach_task_self();
371 ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
373 base::mac::ScopedMachReceiveRight scoped_port_recv(port);
375 ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port,
376 MACH_MSG_TYPE_MAKE_SEND));
377 base::mac::ScopedMachSendRight scoped_port_send(port);
379 BootstrapSandboxPolicy policy;
380 policy.default_rule = Rule(POLICY_ALLOW);
381 policy.rules[kDefaultRuleTestAllow] = Rule(port);
382 policy.rules[kDefaultRuleTestDeny] = Rule(POLICY_DENY_ERROR);
383 sandbox_->RegisterSandboxPolicy(3, policy);
385 base::scoped_nsobject<DistributedNotificationObserver> observer(
386 [[DistributedNotificationObserver alloc] init]);
389 RunChildWithPolicy(3, "DefaultRuleAllow", &pid);
392 [observer waitForNotification];
393 EXPECT_EQ(1, [observer receivedCount]);
394 EXPECT_EQ(pid, [[observer object] intValue]);
396 struct SubstitutePortAckRecv msg;
397 bzero(&msg, sizeof(msg));
398 msg.header.msgh_size = sizeof(msg);
399 msg.header.msgh_local_port = port;
400 kern_return_t kr = mach_msg(&msg.header, MACH_RCV_MSG, 0,
401 msg.header.msgh_size, port,
402 TestTimeouts::tiny_timeout().InMilliseconds(), MACH_PORT_NULL);
403 EXPECT_EQ(KERN_SUCCESS, kr);
405 EXPECT_EQ(0, strncmp(kSubstituteAck, msg.buf, sizeof(msg.buf)));
408 MULTIPROCESS_TEST_MAIN(DefaultRuleAllow) {
409 InitializeXPCIfRequired();
411 [[NSDistributedNotificationCenter defaultCenter]
412 postNotificationName:kTestNotification
413 object:[NSString stringWithFormat:@"%d", getpid()]];
415 mach_port_t port = MACH_PORT_NULL;
416 CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, bootstrap_look_up(bootstrap_port,
417 const_cast<char*>(kDefaultRuleTestDeny), &port));
418 CHECK(port == MACH_PORT_NULL);
420 CHECK_EQ(KERN_SUCCESS, bootstrap_look_up(bootstrap_port,
421 const_cast<char*>(kDefaultRuleTestAllow), &port));
422 CHECK(port != MACH_PORT_NULL);
424 struct SubstitutePortAckSend msg;
425 bzero(&msg, sizeof(msg));
426 msg.header.msgh_size = sizeof(msg);
427 msg.header.msgh_remote_port = port;
428 msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND);
429 strncpy(msg.buf, kSubstituteAck, sizeof(msg.buf));
431 CHECK_EQ(KERN_SUCCESS, mach_msg_send(&msg.header));
436 TEST_F(BootstrapSandboxTest, ChildOutliveSandbox) {
437 const int kTestPolicyId = 1;
438 mach_port_t task = mach_task_self();
440 // Create a server port.
442 ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
444 base::mac::ScopedMachReceiveRight scoped_port_recv(port);
446 ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port,
447 MACH_MSG_TYPE_MAKE_SEND));
448 base::mac::ScopedMachSendRight scoped_port_send(port);
450 // Set up the policy and register the port.
451 BootstrapSandboxPolicy policy(BaselinePolicy());
452 policy.rules["sync"] = Rule(port);
453 sandbox_->RegisterSandboxPolicy(kTestPolicyId, policy);
456 sandbox_->PrepareToForkWithPolicy(kTestPolicyId);
457 base::LaunchOptions options;
458 options.replacement_bootstrap_name = sandbox_->server_bootstrap_name();
459 base::Process process = SpawnChildWithOptions("ChildOutliveSandbox", options);
460 ASSERT_TRUE(process.IsValid());
461 sandbox_->FinishedFork(process.Handle());
463 // Synchronize with the child.
464 mach_msg_empty_rcv_t rcv_msg;
465 bzero(&rcv_msg, sizeof(rcv_msg));
466 kern_return_t kr = mach_msg(&rcv_msg.header, MACH_RCV_MSG, 0,
467 sizeof(rcv_msg), port,
468 TestTimeouts::tiny_timeout().InMilliseconds(), MACH_PORT_NULL);
469 ASSERT_EQ(KERN_SUCCESS, kr) << mach_error_string(kr);
471 // Destroy the sandbox.
474 // Synchronize again with the child.
475 mach_msg_empty_send_t send_msg;
476 bzero(&send_msg, sizeof(send_msg));
477 send_msg.header.msgh_size = sizeof(send_msg);
478 send_msg.header.msgh_remote_port = rcv_msg.header.msgh_remote_port;
479 send_msg.header.msgh_bits =
480 MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND_ONCE);
481 kr = mach_msg(&send_msg.header, MACH_SEND_MSG, send_msg.header.msgh_size, 0,
482 MACH_PORT_NULL, TestTimeouts::tiny_timeout().InMilliseconds(),
484 EXPECT_EQ(KERN_SUCCESS, kr) << mach_error_string(kr);
487 EXPECT_TRUE(process.WaitForExit(&code));
491 MULTIPROCESS_TEST_MAIN(ChildOutliveSandbox) {
492 InitializeXPCIfRequired();
494 // Get the synchronization channel.
495 mach_port_t port = MACH_PORT_NULL;
496 CHECK_EQ(KERN_SUCCESS, bootstrap_look_up(bootstrap_port, "sync", &port));
498 // Create a reply port.
499 mach_port_t reply_port;
500 CHECK_EQ(KERN_SUCCESS, mach_port_allocate(mach_task_self(),
501 MACH_PORT_RIGHT_RECEIVE, &reply_port));
502 base::mac::ScopedMachReceiveRight scoped_reply_port(reply_port);
504 // Send a message to shutdown the sandbox.
505 mach_msg_empty_send_t send_msg;
506 bzero(&send_msg, sizeof(send_msg));
507 send_msg.header.msgh_size = sizeof(send_msg);
508 send_msg.header.msgh_local_port = reply_port;
509 send_msg.header.msgh_remote_port = port;
510 send_msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND,
511 MACH_MSG_TYPE_MAKE_SEND_ONCE);
512 kern_return_t kr = mach_msg_send(&send_msg.header);
513 MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_msg_send";
515 // Flood the server's message queue with messages. There should be some
516 // pending when the sandbox is destroyed.
517 for (int i = 0; i < 20; ++i) {
518 mach_port_t tmp = MACH_PORT_NULL;
519 std::string name = base::StringPrintf("test.%d", i);
520 bootstrap_look_up(bootstrap_port, const_cast<char*>(name.c_str()), &tmp);
523 // Ack that the sandbox has been shutdown.
524 mach_msg_empty_rcv_t rcv_msg;
525 bzero(&rcv_msg, sizeof(rcv_msg));
526 rcv_msg.header.msgh_size = sizeof(rcv_msg);
527 rcv_msg.header.msgh_local_port = reply_port;
528 kr = mach_msg_receive(&rcv_msg.header);
529 MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_msg_receive";
531 // Try to message the sandbox.
532 bootstrap_look_up(bootstrap_port, "test", &port);
537 } // namespace sandbox