[MD settings] moving attached() code
[chromium-blink-merge.git] / sandbox / mac / bootstrap_sandbox_unittest.mm
blob467189e134fa9fd74b9f8dc6756bda4ff3f34451
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>
9 #include <mach/mach.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 {
29  @private
30   int receivedCount_;
31   base::scoped_nsobject<NSString> object_;
33 - (int)receivedCount;
34 - (NSString*)object;
35 - (void)waitForNotification;
36 @end
38 @implementation DistributedNotificationObserver
39 - (id)init {
40   if ((self = [super init])) {
41     [[NSDistributedNotificationCenter defaultCenter]
42         addObserver:self
43            selector:@selector(observeNotification:)
44                name:kTestNotification
45              object:nil];
46   }
47   return self;
50 - (void)dealloc {
51   [[NSDistributedNotificationCenter defaultCenter]
52       removeObserver:self
53                 name:kTestNotification
54               object:nil];
55   [super dealloc];
58 - (int)receivedCount {
59   return receivedCount_;
62 - (NSString*)object {
63   return object_.get();
66 - (void)waitForNotification {
67   object_.reset();
68   CFRunLoopRunInMode(kCFRunLoopDefaultMode,
69       TestTimeouts::action_timeout().InSeconds(), false);
72 - (void)observeNotification:(NSNotification*)notification {
73   ++receivedCount_;
74   object_.reset([[notification object] copy]);
75   CFRunLoopStop(CFRunLoopGetCurrent());
77 @end
79 ////////////////////////////////////////////////////////////////////////////////
81 namespace sandbox {
83 void InitializeXPCIfRequired() {
84   if (base::mac::IsOSYosemiteOrLater())
85     CHECK(InitializeXPC());
88 class BootstrapSandboxTest : public base::MultiProcessTest {
89  public:
90   void SetUp() override {
91     base::MultiProcessTest::SetUp();
93     sandbox_ = BootstrapSandbox::Create();
94     ASSERT_TRUE(sandbox_.get());
95   }
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);
102     return policy;
103   }
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());
116     int code = 0;
117     EXPECT_TRUE(process.WaitForExit(&code));
118     EXPECT_EQ(0, code);
119     if (out_pid)
120       *out_pid = process.Pid();
121   }
123  protected:
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());
136   int code = 0;
137   EXPECT_TRUE(process.WaitForExit(&code));
138   EXPECT_EQ(0, 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
146 // whitelist.
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());
165   // 10.9:
166   policy.rules["com.apple.distributed_notifications@Uv3"] = Rule(POLICY_ALLOW);
167   policy.rules["com.apple.distributed_notifications@1v3"] = Rule(POLICY_ALLOW);
168   // 10.6:
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()]];
187   return 0;
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,
205       &port);
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",
210       &port);
211   CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, kr);
212   CHECK(port == MACH_PORT_NULL);
214   return 0;
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,
230       &port);
231   CHECK_EQ(KERN_SUCCESS, kr);
232   CHECK(port != MACH_PORT_NULL);
233   return 0;
236 struct SubstitutePortAckSend {
237   mach_msg_header_t header;
238   char buf[32];
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();
250   mach_port_t port;
251   ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
252       &port));
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,
257       &send_rights));
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);
264   send_rights = 0;
265   ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
266       &send_rights));
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);
284   send_rights = 0;
285   ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
286       &send_rights));
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));
309   return 0;
312 TEST_F(BootstrapSandboxTest, ForwardMessageInProcess) {
313   mach_port_t task = mach_task_self();
315   mach_port_t port;
316   ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
317       &port));
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,
322       &send_rights));
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);
329   send_rights = 0;
330   ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
331       &send_rights));
332   EXPECT_EQ(1u, send_rights);
334   mach_port_t bp;
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);
345   send_rights = 0;
346   ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
347       &send_rights));
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);
354   send_rights = 0;
355   ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
356       &send_rights));
357   // On 10.6, bootstrap_lookup2 may add an extra right to place it in a per-
358   // process cache.
359   if (base::mac::IsOSSnowLeopard())
360     EXPECT_TRUE(send_rights == 3u || send_rights == 2u) << send_rights;
361   else
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();
373   mach_port_t port;
374   ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
375       &port));
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]);
391   int pid = 0;
392   RunChildWithPolicy(3, "DefaultRuleAllow", &pid);
393   EXPECT_GT(pid, 0);
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));
436   return 0;
439 TEST_F(BootstrapSandboxTest, ChildOutliveSandbox) {
440   const int kTestPolicyId = 1;
441   mach_port_t task = mach_task_self();
443   // Create a server port.
444   mach_port_t port;
445   ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
446       &port));
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);
458   // Launch the child.
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.
475   sandbox_.reset();
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(),
486       MACH_PORT_NULL);
487   EXPECT_EQ(KERN_SUCCESS, kr) << mach_error_string(kr);
489   int code = 0;
490   EXPECT_TRUE(process.WaitForExit(&code));
491   EXPECT_EQ(0, 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);
524   }
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);
537   return 0;
540 }  // namespace sandbox