Add ICU message format support
[chromium-blink-merge.git] / sandbox / mac / bootstrap_sandbox_unittest.mm
blob1eaf4c99fff6a7e10769982bd6e6a4050751783a
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/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 {
28  @private
29   int receivedCount_;
30   base::scoped_nsobject<NSString> object_;
32 - (int)receivedCount;
33 - (NSString*)object;
34 - (void)waitForNotification;
35 @end
37 @implementation DistributedNotificationObserver
38 - (id)init {
39   if ((self = [super init])) {
40     [[NSDistributedNotificationCenter defaultCenter]
41         addObserver:self
42            selector:@selector(observeNotification:)
43                name:kTestNotification
44              object:nil];
45   }
46   return self;
49 - (void)dealloc {
50   [[NSDistributedNotificationCenter defaultCenter]
51       removeObserver:self
52                 name:kTestNotification
53               object:nil];
54   [super dealloc];
57 - (int)receivedCount {
58   return receivedCount_;
61 - (NSString*)object {
62   return object_.get();
65 - (void)waitForNotification {
66   object_.reset();
67   CFRunLoopRunInMode(kCFRunLoopDefaultMode,
68       TestTimeouts::action_timeout().InSeconds(), false);
71 - (void)observeNotification:(NSNotification*)notification {
72   ++receivedCount_;
73   object_.reset([[notification object] copy]);
74   CFRunLoopStop(CFRunLoopGetCurrent());
76 @end
78 ////////////////////////////////////////////////////////////////////////////////
80 namespace sandbox {
82 void InitializeXPCIfRequired() {
83   if (base::mac::IsOSYosemiteOrLater())
84     CHECK(InitializeXPC());
87 class BootstrapSandboxTest : public base::MultiProcessTest {
88  public:
89   void SetUp() override {
90     base::MultiProcessTest::SetUp();
92     sandbox_ = BootstrapSandbox::Create();
93     ASSERT_TRUE(sandbox_.get());
94   }
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);
101     return policy;
102   }
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());
113     int code = 0;
114     EXPECT_TRUE(process.WaitForExit(&code));
115     EXPECT_EQ(0, code);
116     if (out_pid)
117       *out_pid = process.Pid();
118   }
120  protected:
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());
133   int code = 0;
134   EXPECT_TRUE(process.WaitForExit(&code));
135   EXPECT_EQ(0, 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
143 // whitelist.
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());
162   // 10.9:
163   policy.rules["com.apple.distributed_notifications@Uv3"] = Rule(POLICY_ALLOW);
164   policy.rules["com.apple.distributed_notifications@1v3"] = Rule(POLICY_ALLOW);
165   // 10.6:
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()]];
184   return 0;
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,
202       &port);
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",
207       &port);
208   CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, kr);
209   CHECK(port == MACH_PORT_NULL);
211   return 0;
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,
227       &port);
228   CHECK_EQ(KERN_SUCCESS, kr);
229   CHECK(port != MACH_PORT_NULL);
230   return 0;
233 struct SubstitutePortAckSend {
234   mach_msg_header_t header;
235   char buf[32];
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();
247   mach_port_t port;
248   ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
249       &port));
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,
254       &send_rights));
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);
261   send_rights = 0;
262   ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
263       &send_rights));
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);
281   send_rights = 0;
282   ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
283       &send_rights));
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));
306   return 0;
309 TEST_F(BootstrapSandboxTest, ForwardMessageInProcess) {
310   mach_port_t task = mach_task_self();
312   mach_port_t port;
313   ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
314       &port));
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,
319       &send_rights));
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);
326   send_rights = 0;
327   ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
328       &send_rights));
329   EXPECT_EQ(1u, send_rights);
331   mach_port_t bp;
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);
342   send_rights = 0;
343   ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
344       &send_rights));
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);
351   send_rights = 0;
352   ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
353       &send_rights));
354   // On 10.6, bootstrap_lookup2 may add an extra right to place it in a per-
355   // process cache.
356   if (base::mac::IsOSSnowLeopard())
357     EXPECT_TRUE(send_rights == 3u || send_rights == 2u) << send_rights;
358   else
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();
370   mach_port_t port;
371   ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
372       &port));
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]);
388   int pid = 0;
389   RunChildWithPolicy(3, "DefaultRuleAllow", &pid);
390   EXPECT_GT(pid, 0);
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));
433   return 0;
436 TEST_F(BootstrapSandboxTest, ChildOutliveSandbox) {
437   const int kTestPolicyId = 1;
438   mach_port_t task = mach_task_self();
440   // Create a server port.
441   mach_port_t port;
442   ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
443       &port));
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);
455   // Launch the child.
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.
472   sandbox_.reset();
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(),
483       MACH_PORT_NULL);
484   EXPECT_EQ(KERN_SUCCESS, kr) << mach_error_string(kr);
486   int code = 0;
487   EXPECT_TRUE(process.WaitForExit(&code));
488   EXPECT_EQ(0, 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);
521   }
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);
534   return 0;
537 }  // namespace sandbox