• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4
5#include "sandbox/mac/bootstrap_sandbox.h"
6
7#include <CoreFoundation/CoreFoundation.h>
8#import <Foundation/Foundation.h>
9#include <mach/mach.h>
10#include <servers/bootstrap.h>
11
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"
23
24NSString* const kTestNotification = @"org.chromium.bootstrap_sandbox_test";
25
26@interface DistributedNotificationObserver : NSObject {
27 @private
28  int receivedCount_;
29  base::scoped_nsobject<NSString> object_;
30}
31- (int)receivedCount;
32- (NSString*)object;
33- (void)waitForNotification;
34@end
35
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;
46}
47
48- (void)dealloc {
49  [[NSDistributedNotificationCenter defaultCenter]
50      removeObserver:self
51                name:kTestNotification
52              object:nil];
53  [super dealloc];
54}
55
56- (int)receivedCount {
57  return receivedCount_;
58}
59
60- (NSString*)object {
61  return object_.get();
62}
63
64- (void)waitForNotification {
65  object_.reset();
66  CFRunLoopRunInMode(kCFRunLoopDefaultMode,
67      TestTimeouts::action_timeout().InSeconds(), false);
68}
69
70- (void)observeNotification:(NSNotification*)notification {
71  ++receivedCount_;
72  object_.reset([[notification object] copy]);
73  CFRunLoopStop(CFRunLoopGetCurrent());
74}
75@end
76
77////////////////////////////////////////////////////////////////////////////////
78
79namespace sandbox {
80
81class BootstrapSandboxTest : public base::MultiProcessTest {
82 public:
83  virtual void SetUp() OVERRIDE {
84    base::MultiProcessTest::SetUp();
85
86    sandbox_ = BootstrapSandbox::Create();
87    ASSERT_TRUE(sandbox_.get());
88  }
89
90  BootstrapSandboxPolicy BaselinePolicy() {
91    BootstrapSandboxPolicy policy;
92    if (base::mac::IsOSSnowLeopard())
93      policy.rules["com.apple.SecurityServer"] = Rule(POLICY_ALLOW);
94    return policy;
95  }
96
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  }
112
113 protected:
114  scoped_ptr<BootstrapSandbox> sandbox_;
115};
116
117const char kNotificationTestMain[] = "PostNotification";
118
119// Run the test without the sandbox.
120TEST_F(BootstrapSandboxTest, DistributedNotifications_Unsandboxed) {
121  base::scoped_nsobject<DistributedNotificationObserver> observer(
122      [[DistributedNotificationObserver alloc] init]);
123
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);
129
130  [observer waitForNotification];
131  EXPECT_EQ(1, [observer receivedCount]);
132  EXPECT_EQ(pid, [[observer object] intValue]);
133}
134
135// Run the test with the sandbox enabled without notifications on the policy
136// whitelist.
137TEST_F(BootstrapSandboxTest, DistributedNotifications_SandboxDeny) {
138  base::scoped_nsobject<DistributedNotificationObserver> observer(
139      [[DistributedNotificationObserver alloc] init]);
140
141  sandbox_->RegisterSandboxPolicy(1, BaselinePolicy());
142  RunChildWithPolicy(1, kNotificationTestMain, NULL);
143
144  [observer waitForNotification];
145  EXPECT_EQ(0, [observer receivedCount]);
146  EXPECT_EQ(nil, [observer object]);
147}
148
149// Run the test with notifications permitted.
150TEST_F(BootstrapSandboxTest, DistributedNotifications_SandboxAllow) {
151  base::scoped_nsobject<DistributedNotificationObserver> observer(
152      [[DistributedNotificationObserver alloc] init]);
153
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);
162
163  base::ProcessHandle pid;
164  RunChildWithPolicy(2, kNotificationTestMain, &pid);
165
166  [observer waitForNotification];
167  EXPECT_EQ(1, [observer receivedCount]);
168  EXPECT_EQ(pid, [[observer object] intValue]);
169}
170
171MULTIPROCESS_TEST_MAIN(PostNotification) {
172  [[NSDistributedNotificationCenter defaultCenter]
173      postNotificationName:kTestNotification
174                    object:[NSString stringWithFormat:@"%d", getpid()]];
175  return 0;
176}
177
178const char kTestServer[] = "org.chromium.test_bootstrap_server";
179
180TEST_F(BootstrapSandboxTest, PolicyDenyError) {
181  BootstrapSandboxPolicy policy(BaselinePolicy());
182  policy.rules[kTestServer] = Rule(POLICY_DENY_ERROR);
183  sandbox_->RegisterSandboxPolicy(1, policy);
184
185  RunChildWithPolicy(1, "PolicyDenyError", NULL);
186}
187
188MULTIPROCESS_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);
194
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);
199
200  return 0;
201}
202
203TEST_F(BootstrapSandboxTest, PolicyDenyDummyPort) {
204  BootstrapSandboxPolicy policy(BaselinePolicy());
205  policy.rules[kTestServer] = Rule(POLICY_DENY_DUMMY_PORT);
206  sandbox_->RegisterSandboxPolicy(1, policy);
207
208  RunChildWithPolicy(1, "PolicyDenyDummyPort", NULL);
209}
210
211MULTIPROCESS_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;
218}
219
220struct SubstitutePortAckSend {
221  mach_msg_header_t header;
222  char buf[32];
223};
224
225struct SubstitutePortAckRecv : public SubstitutePortAckSend {
226  mach_msg_trailer_t trailer;
227};
228
229const char kSubstituteAck[] = "Hello, this is doge!";
230
231TEST_F(BootstrapSandboxTest, PolicySubstitutePort) {
232  mach_port_t task = mach_task_self();
233
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);
238
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);
243
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);
247
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);
252
253  BootstrapSandboxPolicy policy(BaselinePolicy());
254  policy.rules[kTestServer] = Rule(port);
255  sandbox_->RegisterSandboxPolicy(1, policy);
256
257  RunChildWithPolicy(1, "PolicySubstitutePort", NULL);
258
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);
267
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);
272
273  EXPECT_EQ(0, strncmp(kSubstituteAck, msg.buf, sizeof(msg.buf)));
274}
275
276MULTIPROCESS_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);
281
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));
288
289  CHECK_EQ(KERN_SUCCESS, mach_msg_send(&msg.header));
290
291  return 0;
292}
293
294TEST_F(BootstrapSandboxTest, ForwardMessageInProcess) {
295  mach_port_t task = mach_task_self();
296
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);
301
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);
306
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);
310
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);
315
316  mach_port_t bp;
317  ASSERT_EQ(KERN_SUCCESS, task_get_bootstrap_port(task, &bp));
318  base::mac::ScopedMachSendRight scoped_bp(bp);
319
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);
326
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);
331
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);
335
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);
345}
346
347const char kDefaultRuleTestAllow[] =
348    "org.chromium.sandbox.test.DefaultRuleAllow";
349const char kDefaultRuleTestDeny[] =
350    "org.chromium.sandbox.test.DefaultRuleAllow.Deny";
351
352TEST_F(BootstrapSandboxTest, DefaultRuleAllow) {
353  mach_port_t task = mach_task_self();
354
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);
359
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);
363
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);
369
370  base::scoped_nsobject<DistributedNotificationObserver> observer(
371      [[DistributedNotificationObserver alloc] init]);
372
373  int pid = 0;
374  RunChildWithPolicy(3, "DefaultRuleAllow", &pid);
375  EXPECT_GT(pid, 0);
376
377  [observer waitForNotification];
378  EXPECT_EQ(1, [observer receivedCount]);
379  EXPECT_EQ(pid, [[observer object] intValue]);
380
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);
389
390  EXPECT_EQ(0, strncmp(kSubstituteAck, msg.buf, sizeof(msg.buf)));
391}
392
393MULTIPROCESS_TEST_MAIN(DefaultRuleAllow) {
394  [[NSDistributedNotificationCenter defaultCenter]
395      postNotificationName:kTestNotification
396                    object:[NSString stringWithFormat:@"%d", getpid()]];
397
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);
402
403  CHECK_EQ(KERN_SUCCESS, bootstrap_look_up(bootstrap_port,
404      const_cast<char*>(kDefaultRuleTestAllow), &port));
405  CHECK(port != MACH_PORT_NULL);
406
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));
413
414  CHECK_EQ(KERN_SUCCESS, mach_msg_send(&msg.header));
415
416  return 0;
417}
418
419TEST_F(BootstrapSandboxTest, ChildOutliveSandbox) {
420  const int kTestPolicyId = 1;
421  mach_port_t task = mach_task_self();
422
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);
428
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);
432
433  // Set up the policy and register the port.
434  BootstrapSandboxPolicy policy(BaselinePolicy());
435  policy.rules["sync"] = Rule(port);
436  sandbox_->RegisterSandboxPolicy(kTestPolicyId, policy);
437
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);
446
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);
454
455  // Destroy the sandbox.
456  sandbox_.reset();
457
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);
469
470  int code = 0;
471  EXPECT_TRUE(base::WaitForExitCode(pid, &code));
472  EXPECT_EQ(0, code);
473}
474
475MULTIPROCESS_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));
479
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);
485
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";
496
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  }
504
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";
512
513  // Try to message the sandbox.
514  bootstrap_look_up(bootstrap_port, "test", &port);
515
516  return 0;
517}
518
519}  // namespace sandbox
520