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 #import "testing/gtest_mac.h"
22 #include "testing/multiprocess_func_list.h"
24 NSString* const kTestNotification = @"org.chromium.bootstrap_sandbox_test";
26 @interface DistributedNotificationObserver : NSObject {
29 base::scoped_nsobject<NSString> object_;
33 - (void)waitForNotification;
36 @implementation DistributedNotificationObserver
38 if ((self = [super init])) {
39 [[NSDistributedNotificationCenter defaultCenter]
41 selector:@selector(observeNotification:)
42 name:kTestNotification
49 [[NSDistributedNotificationCenter defaultCenter]
51 name:kTestNotification
56 - (int)receivedCount {
57 return receivedCount_;
64 - (void)waitForNotification {
66 CFRunLoopRunInMode(kCFRunLoopDefaultMode,
67 TestTimeouts::action_timeout().InSeconds(), false);
70 - (void)observeNotification:(NSNotification*)notification {
72 object_.reset([[notification object] copy]);
73 CFRunLoopStop(CFRunLoopGetCurrent());
77 ////////////////////////////////////////////////////////////////////////////////
81 class BootstrapSandboxTest : public base::MultiProcessTest {
83 void SetUp() override {
84 base::MultiProcessTest::SetUp();
86 sandbox_ = BootstrapSandbox::Create();
87 ASSERT_TRUE(sandbox_.get());
90 BootstrapSandboxPolicy BaselinePolicy() {
91 BootstrapSandboxPolicy policy;
92 if (base::mac::IsOSSnowLeopard())
93 policy.rules["com.apple.SecurityServer"] = Rule(POLICY_ALLOW);
97 void RunChildWithPolicy(int policy_id,
98 const char* child_name,
99 base::ProcessHandle* out_pid) {
100 sandbox_->PrepareToForkWithPolicy(policy_id);
101 base::LaunchOptions options;
102 options.replacement_bootstrap_name = sandbox_->server_bootstrap_name();
103 base::Process process = SpawnChildWithOptions(child_name, options);
104 ASSERT_TRUE(process.IsValid());
105 sandbox_->FinishedFork(process.Handle());
107 EXPECT_TRUE(process.WaitForExit(&code));
110 *out_pid = process.Pid();
114 scoped_ptr<BootstrapSandbox> sandbox_;
117 const char kNotificationTestMain[] = "PostNotification";
119 // Run the test without the sandbox.
120 TEST_F(BootstrapSandboxTest, DistributedNotifications_Unsandboxed) {
121 base::scoped_nsobject<DistributedNotificationObserver> observer(
122 [[DistributedNotificationObserver alloc] init]);
124 base::Process process = SpawnChild(kNotificationTestMain);
125 ASSERT_TRUE(process.IsValid());
127 EXPECT_TRUE(process.WaitForExit(&code));
130 [observer waitForNotification];
131 EXPECT_EQ(1, [observer receivedCount]);
132 EXPECT_EQ(process.Pid(), [[observer object] intValue]);
135 // Run the test with the sandbox enabled without notifications on the policy
137 TEST_F(BootstrapSandboxTest, DistributedNotifications_SandboxDeny) {
138 base::scoped_nsobject<DistributedNotificationObserver> observer(
139 [[DistributedNotificationObserver alloc] init]);
141 sandbox_->RegisterSandboxPolicy(1, BaselinePolicy());
142 RunChildWithPolicy(1, kNotificationTestMain, NULL);
144 [observer waitForNotification];
145 EXPECT_EQ(0, [observer receivedCount]);
146 EXPECT_EQ(nil, [observer object]);
149 // Run the test with notifications permitted.
150 TEST_F(BootstrapSandboxTest, DistributedNotifications_SandboxAllow) {
151 base::scoped_nsobject<DistributedNotificationObserver> observer(
152 [[DistributedNotificationObserver alloc] init]);
154 BootstrapSandboxPolicy policy(BaselinePolicy());
156 policy.rules["com.apple.distributed_notifications@Uv3"] = Rule(POLICY_ALLOW);
157 policy.rules["com.apple.distributed_notifications@1v3"] = Rule(POLICY_ALLOW);
159 policy.rules["com.apple.system.notification_center"] = Rule(POLICY_ALLOW);
160 policy.rules["com.apple.distributed_notifications.2"] = Rule(POLICY_ALLOW);
161 sandbox_->RegisterSandboxPolicy(2, policy);
163 base::ProcessHandle pid;
164 RunChildWithPolicy(2, kNotificationTestMain, &pid);
166 [observer waitForNotification];
167 EXPECT_EQ(1, [observer receivedCount]);
168 EXPECT_EQ(pid, [[observer object] intValue]);
171 MULTIPROCESS_TEST_MAIN(PostNotification) {
172 [[NSDistributedNotificationCenter defaultCenter]
173 postNotificationName:kTestNotification
174 object:[NSString stringWithFormat:@"%d", getpid()]];
178 const char kTestServer[] = "org.chromium.test_bootstrap_server";
180 TEST_F(BootstrapSandboxTest, PolicyDenyError) {
181 BootstrapSandboxPolicy policy(BaselinePolicy());
182 policy.rules[kTestServer] = Rule(POLICY_DENY_ERROR);
183 sandbox_->RegisterSandboxPolicy(1, policy);
185 RunChildWithPolicy(1, "PolicyDenyError", NULL);
188 MULTIPROCESS_TEST_MAIN(PolicyDenyError) {
189 mach_port_t port = MACH_PORT_NULL;
190 kern_return_t kr = bootstrap_look_up(bootstrap_port, kTestServer,
192 CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, kr);
193 CHECK(port == MACH_PORT_NULL);
195 kr = bootstrap_look_up(bootstrap_port, "org.chromium.some_other_server",
197 CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, kr);
198 CHECK(port == MACH_PORT_NULL);
203 TEST_F(BootstrapSandboxTest, PolicyDenyDummyPort) {
204 BootstrapSandboxPolicy policy(BaselinePolicy());
205 policy.rules[kTestServer] = Rule(POLICY_DENY_DUMMY_PORT);
206 sandbox_->RegisterSandboxPolicy(1, policy);
208 RunChildWithPolicy(1, "PolicyDenyDummyPort", NULL);
211 MULTIPROCESS_TEST_MAIN(PolicyDenyDummyPort) {
212 mach_port_t port = MACH_PORT_NULL;
213 kern_return_t kr = bootstrap_look_up(bootstrap_port, kTestServer,
215 CHECK_EQ(KERN_SUCCESS, kr);
216 CHECK(port != MACH_PORT_NULL);
220 struct SubstitutePortAckSend {
221 mach_msg_header_t header;
225 struct SubstitutePortAckRecv : public SubstitutePortAckSend {
226 mach_msg_trailer_t trailer;
229 const char kSubstituteAck[] = "Hello, this is doge!";
231 TEST_F(BootstrapSandboxTest, PolicySubstitutePort) {
232 mach_port_t task = mach_task_self();
235 ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
237 base::mac::ScopedMachReceiveRight scoped_port(port);
239 mach_port_urefs_t send_rights = 0;
240 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
242 EXPECT_EQ(0u, send_rights);
244 ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port,
245 MACH_MSG_TYPE_MAKE_SEND));
246 base::mac::ScopedMachSendRight scoped_port_send(port);
249 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
251 EXPECT_EQ(1u, send_rights);
253 BootstrapSandboxPolicy policy(BaselinePolicy());
254 policy.rules[kTestServer] = Rule(port);
255 sandbox_->RegisterSandboxPolicy(1, policy);
257 RunChildWithPolicy(1, "PolicySubstitutePort", NULL);
259 struct SubstitutePortAckRecv msg;
260 bzero(&msg, sizeof(msg));
261 msg.header.msgh_size = sizeof(msg);
262 msg.header.msgh_local_port = port;
263 kern_return_t kr = mach_msg(&msg.header, MACH_RCV_MSG, 0,
264 msg.header.msgh_size, port,
265 TestTimeouts::tiny_timeout().InMilliseconds(), MACH_PORT_NULL);
266 EXPECT_EQ(KERN_SUCCESS, kr);
269 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
271 EXPECT_EQ(1u, send_rights);
273 EXPECT_EQ(0, strncmp(kSubstituteAck, msg.buf, sizeof(msg.buf)));
276 MULTIPROCESS_TEST_MAIN(PolicySubstitutePort) {
277 mach_port_t port = MACH_PORT_NULL;
278 kern_return_t kr = bootstrap_look_up(bootstrap_port, kTestServer, &port);
279 CHECK_EQ(KERN_SUCCESS, kr);
280 CHECK(port != MACH_PORT_NULL);
282 struct SubstitutePortAckSend msg;
283 bzero(&msg, sizeof(msg));
284 msg.header.msgh_size = sizeof(msg);
285 msg.header.msgh_remote_port = port;
286 msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND);
287 strncpy(msg.buf, kSubstituteAck, sizeof(msg.buf));
289 CHECK_EQ(KERN_SUCCESS, mach_msg_send(&msg.header));
294 TEST_F(BootstrapSandboxTest, ForwardMessageInProcess) {
295 mach_port_t task = mach_task_self();
298 ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
300 base::mac::ScopedMachReceiveRight scoped_port_recv(port);
302 mach_port_urefs_t send_rights = 0;
303 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
305 EXPECT_EQ(0u, send_rights);
307 ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port,
308 MACH_MSG_TYPE_MAKE_SEND));
309 base::mac::ScopedMachSendRight scoped_port_send(port);
312 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
314 EXPECT_EQ(1u, send_rights);
317 ASSERT_EQ(KERN_SUCCESS, task_get_bootstrap_port(task, &bp));
318 base::mac::ScopedMachSendRight scoped_bp(bp);
320 char service_name[] = "org.chromium.sandbox.test.ForwardMessageInProcess";
321 #pragma GCC diagnostic push
322 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
323 kern_return_t kr = bootstrap_register(bp, service_name, port);
324 #pragma GCC diagnostic pop
325 EXPECT_EQ(KERN_SUCCESS, kr);
328 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
330 EXPECT_EQ(1u, send_rights);
332 mach_port_t service_port;
333 EXPECT_EQ(KERN_SUCCESS, bootstrap_look_up(bp, service_name, &service_port));
334 base::mac::ScopedMachSendRight scoped_service_port(service_port);
337 ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
339 // On 10.6, bootstrap_lookup2 may add an extra right to place it in a per-
341 if (base::mac::IsOSSnowLeopard())
342 EXPECT_TRUE(send_rights == 3u || send_rights == 2u) << send_rights;
344 EXPECT_EQ(2u, send_rights);
347 const char kDefaultRuleTestAllow[] =
348 "org.chromium.sandbox.test.DefaultRuleAllow";
349 const char kDefaultRuleTestDeny[] =
350 "org.chromium.sandbox.test.DefaultRuleAllow.Deny";
352 TEST_F(BootstrapSandboxTest, DefaultRuleAllow) {
353 mach_port_t task = mach_task_self();
356 ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
358 base::mac::ScopedMachReceiveRight scoped_port_recv(port);
360 ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port,
361 MACH_MSG_TYPE_MAKE_SEND));
362 base::mac::ScopedMachSendRight scoped_port_send(port);
364 BootstrapSandboxPolicy policy;
365 policy.default_rule = Rule(POLICY_ALLOW);
366 policy.rules[kDefaultRuleTestAllow] = Rule(port);
367 policy.rules[kDefaultRuleTestDeny] = Rule(POLICY_DENY_ERROR);
368 sandbox_->RegisterSandboxPolicy(3, policy);
370 base::scoped_nsobject<DistributedNotificationObserver> observer(
371 [[DistributedNotificationObserver alloc] init]);
374 RunChildWithPolicy(3, "DefaultRuleAllow", &pid);
377 [observer waitForNotification];
378 EXPECT_EQ(1, [observer receivedCount]);
379 EXPECT_EQ(pid, [[observer object] intValue]);
381 struct SubstitutePortAckRecv msg;
382 bzero(&msg, sizeof(msg));
383 msg.header.msgh_size = sizeof(msg);
384 msg.header.msgh_local_port = port;
385 kern_return_t kr = mach_msg(&msg.header, MACH_RCV_MSG, 0,
386 msg.header.msgh_size, port,
387 TestTimeouts::tiny_timeout().InMilliseconds(), MACH_PORT_NULL);
388 EXPECT_EQ(KERN_SUCCESS, kr);
390 EXPECT_EQ(0, strncmp(kSubstituteAck, msg.buf, sizeof(msg.buf)));
393 MULTIPROCESS_TEST_MAIN(DefaultRuleAllow) {
394 [[NSDistributedNotificationCenter defaultCenter]
395 postNotificationName:kTestNotification
396 object:[NSString stringWithFormat:@"%d", getpid()]];
398 mach_port_t port = MACH_PORT_NULL;
399 CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, bootstrap_look_up(bootstrap_port,
400 const_cast<char*>(kDefaultRuleTestDeny), &port));
401 CHECK(port == MACH_PORT_NULL);
403 CHECK_EQ(KERN_SUCCESS, bootstrap_look_up(bootstrap_port,
404 const_cast<char*>(kDefaultRuleTestAllow), &port));
405 CHECK(port != MACH_PORT_NULL);
407 struct SubstitutePortAckSend msg;
408 bzero(&msg, sizeof(msg));
409 msg.header.msgh_size = sizeof(msg);
410 msg.header.msgh_remote_port = port;
411 msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND);
412 strncpy(msg.buf, kSubstituteAck, sizeof(msg.buf));
414 CHECK_EQ(KERN_SUCCESS, mach_msg_send(&msg.header));
419 TEST_F(BootstrapSandboxTest, ChildOutliveSandbox) {
420 const int kTestPolicyId = 1;
421 mach_port_t task = mach_task_self();
423 // Create a server port.
425 ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
427 base::mac::ScopedMachReceiveRight scoped_port_recv(port);
429 ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port,
430 MACH_MSG_TYPE_MAKE_SEND));
431 base::mac::ScopedMachSendRight scoped_port_send(port);
433 // Set up the policy and register the port.
434 BootstrapSandboxPolicy policy(BaselinePolicy());
435 policy.rules["sync"] = Rule(port);
436 sandbox_->RegisterSandboxPolicy(kTestPolicyId, policy);
439 sandbox_->PrepareToForkWithPolicy(kTestPolicyId);
440 base::LaunchOptions options;
441 options.replacement_bootstrap_name = sandbox_->server_bootstrap_name();
442 base::Process process = SpawnChildWithOptions("ChildOutliveSandbox", options);
443 ASSERT_TRUE(process.IsValid());
444 sandbox_->FinishedFork(process.Handle());
446 // Synchronize with the child.
447 mach_msg_empty_rcv_t rcv_msg;
448 bzero(&rcv_msg, sizeof(rcv_msg));
449 kern_return_t kr = mach_msg(&rcv_msg.header, MACH_RCV_MSG, 0,
450 sizeof(rcv_msg), port,
451 TestTimeouts::tiny_timeout().InMilliseconds(), MACH_PORT_NULL);
452 ASSERT_EQ(KERN_SUCCESS, kr) << mach_error_string(kr);
454 // Destroy the sandbox.
457 // Synchronize again with the child.
458 mach_msg_empty_send_t send_msg;
459 bzero(&send_msg, sizeof(send_msg));
460 send_msg.header.msgh_size = sizeof(send_msg);
461 send_msg.header.msgh_remote_port = rcv_msg.header.msgh_remote_port;
462 send_msg.header.msgh_bits =
463 MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND_ONCE);
464 kr = mach_msg(&send_msg.header, MACH_SEND_MSG, send_msg.header.msgh_size, 0,
465 MACH_PORT_NULL, TestTimeouts::tiny_timeout().InMilliseconds(),
467 EXPECT_EQ(KERN_SUCCESS, kr) << mach_error_string(kr);
470 EXPECT_TRUE(process.WaitForExit(&code));
474 MULTIPROCESS_TEST_MAIN(ChildOutliveSandbox) {
475 // Get the synchronization channel.
476 mach_port_t port = MACH_PORT_NULL;
477 CHECK_EQ(KERN_SUCCESS, bootstrap_look_up(bootstrap_port, "sync", &port));
479 // Create a reply port.
480 mach_port_t reply_port;
481 CHECK_EQ(KERN_SUCCESS, mach_port_allocate(mach_task_self(),
482 MACH_PORT_RIGHT_RECEIVE, &reply_port));
483 base::mac::ScopedMachReceiveRight scoped_reply_port(reply_port);
485 // Send a message to shutdown the sandbox.
486 mach_msg_empty_send_t send_msg;
487 bzero(&send_msg, sizeof(send_msg));
488 send_msg.header.msgh_size = sizeof(send_msg);
489 send_msg.header.msgh_local_port = reply_port;
490 send_msg.header.msgh_remote_port = port;
491 send_msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND,
492 MACH_MSG_TYPE_MAKE_SEND_ONCE);
493 kern_return_t kr = mach_msg_send(&send_msg.header);
494 MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_msg_send";
496 // Flood the server's message queue with messages. There should be some
497 // pending when the sandbox is destroyed.
498 for (int i = 0; i < 20; ++i) {
499 mach_port_t tmp = MACH_PORT_NULL;
500 std::string name = base::StringPrintf("test.%d", i);
501 bootstrap_look_up(bootstrap_port, const_cast<char*>(name.c_str()), &tmp);
504 // Ack that the sandbox has been shutdown.
505 mach_msg_empty_rcv_t rcv_msg;
506 bzero(&rcv_msg, sizeof(rcv_msg));
507 rcv_msg.header.msgh_size = sizeof(rcv_msg);
508 rcv_msg.header.msgh_local_port = reply_port;
509 kr = mach_msg_receive(&rcv_msg.header);
510 MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_msg_receive";
512 // Try to message the sandbox.
513 bootstrap_look_up(bootstrap_port, "test", &port);
518 } // namespace sandbox