Blink roll 25b6bd3a7a131ffe68d809546ad1a20707915cdc:3a503f41ae42e5b79cfcd2ff10e65afde...
[chromium-blink-merge.git] / sandbox / mac / bootstrap_sandbox_unittest.mm
blob5621211a342a4cbdbd922ec76d37045fafc4df93
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_nsobject.h"
16 #include "base/mac/scoped_mach_port.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 {
27  @private
28   int receivedCount_;
29   base::scoped_nsobject<NSString> object_;
31 - (int)receivedCount;
32 - (NSString*)object;
33 - (void)waitForNotification;
34 @end
36 @implementation DistributedNotificationObserver
37 - (id)init {
38   if ((self = [super init])) {
39     [[NSDistributedNotificationCenter defaultCenter]
40         addObserver:self
41            selector:@selector(observeNotification:)
42                name:kTestNotification
43              object:nil];
44   }
45   return self;
48 - (void)dealloc {
49   [[NSDistributedNotificationCenter defaultCenter]
50       removeObserver:self
51                 name:kTestNotification
52               object:nil];
53   [super dealloc];
56 - (int)receivedCount {
57   return receivedCount_;
60 - (NSString*)object {
61   return object_.get();
64 - (void)waitForNotification {
65   object_.reset();
66   CFRunLoopRunInMode(kCFRunLoopDefaultMode,
67       TestTimeouts::action_timeout().InSeconds(), false);
70 - (void)observeNotification:(NSNotification*)notification {
71   ++receivedCount_;
72   object_.reset([[notification object] copy]);
73   CFRunLoopStop(CFRunLoopGetCurrent());
75 @end
77 ////////////////////////////////////////////////////////////////////////////////
79 namespace sandbox {
81 class BootstrapSandboxTest : public base::MultiProcessTest {
82  public:
83   virtual void SetUp() override {
84     base::MultiProcessTest::SetUp();
86     sandbox_ = BootstrapSandbox::Create();
87     ASSERT_TRUE(sandbox_.get());
88   }
90   BootstrapSandboxPolicy BaselinePolicy() {
91     BootstrapSandboxPolicy policy;
92     if (base::mac::IsOSSnowLeopard())
93       policy.rules["com.apple.SecurityServer"] = Rule(POLICY_ALLOW);
94     return policy;
95   }
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::ProcessHandle pid = SpawnChildWithOptions(child_name, options);
104     ASSERT_GT(pid, 0);
105     sandbox_->FinishedFork(pid);
106     int code = 0;
107     EXPECT_TRUE(base::WaitForExitCode(pid, &code));
108     EXPECT_EQ(0, code);
109     if (out_pid)
110       *out_pid = pid;
111   }
113  protected:
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::ProcessHandle pid = SpawnChild(kNotificationTestMain);
125   ASSERT_GT(pid, 0);
126   int code = 0;
127   EXPECT_TRUE(base::WaitForExitCode(pid, &code));
128   EXPECT_EQ(0, code);
130   [observer waitForNotification];
131   EXPECT_EQ(1, [observer receivedCount]);
132   EXPECT_EQ(pid, [[observer object] intValue]);
135 // Run the test with the sandbox enabled without notifications on the policy
136 // whitelist.
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());
155   // 10.9:
156   policy.rules["com.apple.distributed_notifications@Uv3"] = Rule(POLICY_ALLOW);
157   policy.rules["com.apple.distributed_notifications@1v3"] = Rule(POLICY_ALLOW);
158   // 10.6:
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()]];
175   return 0;
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,
191       &port);
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",
196       &port);
197   CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, kr);
198   CHECK(port == MACH_PORT_NULL);
200   return 0;
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,
214       &port);
215   CHECK_EQ(KERN_SUCCESS, kr);
216   CHECK(port != MACH_PORT_NULL);
217   return 0;
220 struct SubstitutePortAckSend {
221   mach_msg_header_t header;
222   char buf[32];
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();
234   mach_port_t port;
235   ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
236       &port));
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,
241       &send_rights));
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);
248   send_rights = 0;
249   ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
250       &send_rights));
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);
268   send_rights = 0;
269   ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
270       &send_rights));
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));
291   return 0;
294 TEST_F(BootstrapSandboxTest, ForwardMessageInProcess) {
295   mach_port_t task = mach_task_self();
297   mach_port_t port;
298   ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
299       &port));
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,
304       &send_rights));
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);
311   send_rights = 0;
312   ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
313       &send_rights));
314   EXPECT_EQ(1u, send_rights);
316   mach_port_t bp;
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);
327   send_rights = 0;
328   ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
329       &send_rights));
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);
336   send_rights = 0;
337   ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
338       &send_rights));
339   // On 10.6, bootstrap_lookup2 may add an extra right to place it in a per-
340   // process cache.
341   if (base::mac::IsOSSnowLeopard())
342     EXPECT_TRUE(send_rights == 3u || send_rights == 2u) << send_rights;
343   else
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();
355   mach_port_t port;
356   ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
357       &port));
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]);
373   int pid = 0;
374   RunChildWithPolicy(3, "DefaultRuleAllow", &pid);
375   EXPECT_GT(pid, 0);
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));
416   return 0;
419 TEST_F(BootstrapSandboxTest, ChildOutliveSandbox) {
420   const int kTestPolicyId = 1;
421   mach_port_t task = mach_task_self();
423   // Create a server port.
424   mach_port_t port;
425   ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
426       &port));
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);
438   // Launch the child.
439   sandbox_->PrepareToForkWithPolicy(kTestPolicyId);
440   base::LaunchOptions options;
441   options.replacement_bootstrap_name = sandbox_->server_bootstrap_name();
442   base::ProcessHandle pid =
443       SpawnChildWithOptions("ChildOutliveSandbox", options);
444   ASSERT_GT(pid, 0);
445   sandbox_->FinishedFork(pid);
447   // Synchronize with the child.
448   mach_msg_empty_rcv_t rcv_msg;
449   bzero(&rcv_msg, sizeof(rcv_msg));
450   kern_return_t kr = mach_msg(&rcv_msg.header, MACH_RCV_MSG, 0,
451       sizeof(rcv_msg), port,
452       TestTimeouts::tiny_timeout().InMilliseconds(), MACH_PORT_NULL);
453   ASSERT_EQ(KERN_SUCCESS, kr) << mach_error_string(kr);
455   // Destroy the sandbox.
456   sandbox_.reset();
458   // Synchronize again with the child.
459   mach_msg_empty_send_t send_msg;
460   bzero(&send_msg, sizeof(send_msg));
461   send_msg.header.msgh_size = sizeof(send_msg);
462   send_msg.header.msgh_remote_port = rcv_msg.header.msgh_remote_port;
463   send_msg.header.msgh_bits =
464       MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND_ONCE);
465   kr = mach_msg(&send_msg.header, MACH_SEND_MSG, send_msg.header.msgh_size, 0,
466       MACH_PORT_NULL, TestTimeouts::tiny_timeout().InMilliseconds(),
467       MACH_PORT_NULL);
468   EXPECT_EQ(KERN_SUCCESS, kr) << mach_error_string(kr);
470   int code = 0;
471   EXPECT_TRUE(base::WaitForExitCode(pid, &code));
472   EXPECT_EQ(0, code);
475 MULTIPROCESS_TEST_MAIN(ChildOutliveSandbox) {
476   // Get the synchronization channel.
477   mach_port_t port = MACH_PORT_NULL;
478   CHECK_EQ(KERN_SUCCESS, bootstrap_look_up(bootstrap_port, "sync", &port));
480   // Create a reply port.
481   mach_port_t reply_port;
482   CHECK_EQ(KERN_SUCCESS, mach_port_allocate(mach_task_self(),
483       MACH_PORT_RIGHT_RECEIVE, &reply_port));
484   base::mac::ScopedMachReceiveRight scoped_reply_port(reply_port);
486   // Send a message to shutdown the sandbox.
487   mach_msg_empty_send_t send_msg;
488   bzero(&send_msg, sizeof(send_msg));
489   send_msg.header.msgh_size = sizeof(send_msg);
490   send_msg.header.msgh_local_port = reply_port;
491   send_msg.header.msgh_remote_port = port;
492   send_msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND,
493                                              MACH_MSG_TYPE_MAKE_SEND_ONCE);
494   kern_return_t kr = mach_msg_send(&send_msg.header);
495   MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_msg_send";
497   // Flood the server's message queue with messages. There should be some
498   // pending when the sandbox is destroyed.
499   for (int i = 0; i < 20; ++i) {
500     mach_port_t tmp = MACH_PORT_NULL;
501     std::string name = base::StringPrintf("test.%d", i);
502     bootstrap_look_up(bootstrap_port, const_cast<char*>(name.c_str()), &tmp);
503   }
505   // Ack that the sandbox has been shutdown.
506   mach_msg_empty_rcv_t rcv_msg;
507   bzero(&rcv_msg, sizeof(rcv_msg));
508   rcv_msg.header.msgh_size = sizeof(rcv_msg);
509   rcv_msg.header.msgh_local_port = reply_port;
510   kr = mach_msg_receive(&rcv_msg.header);
511   MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_msg_receive";
513   // Try to message the sandbox.
514   bootstrap_look_up(bootstrap_port, "test", &port);
516   return 0;
519 }  // namespace sandbox