• 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/scoped_nsobject.h"
15#include "base/mac/scoped_mach_port.h"
16#include "base/process/kill.h"
17#include "base/test/multiprocess_test.h"
18#include "base/test/test_timeouts.h"
19#import "testing/gtest_mac.h"
20#include "testing/multiprocess_func_list.h"
21
22NSString* const kTestNotification = @"org.chromium.bootstrap_sandbox_test";
23
24@interface DistributedNotificationObserver : NSObject {
25 @private
26  int receivedCount_;
27  base::scoped_nsobject<NSString> object_;
28}
29- (int)receivedCount;
30- (NSString*)object;
31- (void)waitForNotification;
32@end
33
34@implementation DistributedNotificationObserver
35- (id)init {
36  if ((self = [super init])) {
37    [[NSDistributedNotificationCenter defaultCenter]
38        addObserver:self
39           selector:@selector(observeNotification:)
40               name:kTestNotification
41             object:nil];
42  }
43  return self;
44}
45
46- (void)dealloc {
47  [[NSDistributedNotificationCenter defaultCenter]
48      removeObserver:self
49                name:kTestNotification
50              object:nil];
51  [super dealloc];
52}
53
54- (int)receivedCount {
55  return receivedCount_;
56}
57
58- (NSString*)object {
59  return object_.get();
60}
61
62- (void)waitForNotification {
63  object_.reset();
64  CFRunLoopRunInMode(kCFRunLoopDefaultMode,
65      TestTimeouts::action_timeout().InSeconds(), false);
66}
67
68- (void)observeNotification:(NSNotification*)notification {
69  ++receivedCount_;
70  object_.reset([[notification object] copy]);
71  CFRunLoopStop(CFRunLoopGetCurrent());
72}
73@end
74
75////////////////////////////////////////////////////////////////////////////////
76
77namespace sandbox {
78
79class BootstrapSandboxTest : public base::MultiProcessTest {
80 public:
81  virtual void SetUp() OVERRIDE {
82    base::MultiProcessTest::SetUp();
83
84    sandbox_ = BootstrapSandbox::Create();
85    ASSERT_TRUE(sandbox_.get());
86  }
87
88  BootstrapSandboxPolicy BaselinePolicy() {
89    BootstrapSandboxPolicy policy;
90    if (base::mac::IsOSSnowLeopard())
91      policy.rules["com.apple.SecurityServer"] = Rule(POLICY_ALLOW);
92    return policy;
93  }
94
95  void RunChildWithPolicy(int policy_id,
96                          const char* child_name,
97                          base::ProcessHandle* out_pid) {
98    sandbox_->PrepareToForkWithPolicy(policy_id);
99    base::LaunchOptions options;
100    options.replacement_bootstrap_name = sandbox_->server_bootstrap_name();
101    base::ProcessHandle pid = SpawnChildWithOptions(child_name, options);
102    ASSERT_GT(pid, 0);
103    sandbox_->FinishedFork(pid);
104    int code = 0;
105    EXPECT_TRUE(base::WaitForExitCode(pid, &code));
106    EXPECT_EQ(0, code);
107    if (out_pid)
108      *out_pid = pid;
109  }
110
111 protected:
112  scoped_ptr<BootstrapSandbox> sandbox_;
113};
114
115const char kNotificationTestMain[] = "PostNotification";
116
117// Run the test without the sandbox.
118TEST_F(BootstrapSandboxTest, DistributedNotifications_Unsandboxed) {
119  base::scoped_nsobject<DistributedNotificationObserver> observer(
120      [[DistributedNotificationObserver alloc] init]);
121
122  base::ProcessHandle pid = SpawnChild(kNotificationTestMain);
123  ASSERT_GT(pid, 0);
124  int code = 0;
125  EXPECT_TRUE(base::WaitForExitCode(pid, &code));
126  EXPECT_EQ(0, code);
127
128  [observer waitForNotification];
129  EXPECT_EQ(1, [observer receivedCount]);
130  EXPECT_EQ(pid, [[observer object] intValue]);
131}
132
133// Run the test with the sandbox enabled without notifications on the policy
134// whitelist.
135TEST_F(BootstrapSandboxTest, DistributedNotifications_SandboxDeny) {
136  base::scoped_nsobject<DistributedNotificationObserver> observer(
137      [[DistributedNotificationObserver alloc] init]);
138
139  sandbox_->RegisterSandboxPolicy(1, BaselinePolicy());
140  RunChildWithPolicy(1, kNotificationTestMain, NULL);
141
142  [observer waitForNotification];
143  EXPECT_EQ(0, [observer receivedCount]);
144  EXPECT_EQ(nil, [observer object]);
145}
146
147// Run the test with notifications permitted.
148TEST_F(BootstrapSandboxTest, DistributedNotifications_SandboxAllow) {
149  base::scoped_nsobject<DistributedNotificationObserver> observer(
150      [[DistributedNotificationObserver alloc] init]);
151
152  BootstrapSandboxPolicy policy(BaselinePolicy());
153  // 10.9:
154  policy.rules["com.apple.distributed_notifications@Uv3"] = Rule(POLICY_ALLOW);
155  policy.rules["com.apple.distributed_notifications@1v3"] = Rule(POLICY_ALLOW);
156  // 10.6:
157  policy.rules["com.apple.system.notification_center"] = Rule(POLICY_ALLOW);
158  policy.rules["com.apple.distributed_notifications.2"] = Rule(POLICY_ALLOW);
159  sandbox_->RegisterSandboxPolicy(2, policy);
160
161  base::ProcessHandle pid;
162  RunChildWithPolicy(2, kNotificationTestMain, &pid);
163
164  [observer waitForNotification];
165  EXPECT_EQ(1, [observer receivedCount]);
166  EXPECT_EQ(pid, [[observer object] intValue]);
167}
168
169MULTIPROCESS_TEST_MAIN(PostNotification) {
170  [[NSDistributedNotificationCenter defaultCenter]
171      postNotificationName:kTestNotification
172                    object:[NSString stringWithFormat:@"%d", getpid()]];
173  return 0;
174}
175
176const char kTestServer[] = "org.chromium.test_bootstrap_server";
177
178TEST_F(BootstrapSandboxTest, PolicyDenyError) {
179  BootstrapSandboxPolicy policy(BaselinePolicy());
180  policy.rules[kTestServer] = Rule(POLICY_DENY_ERROR);
181  sandbox_->RegisterSandboxPolicy(1, policy);
182
183  RunChildWithPolicy(1, "PolicyDenyError", NULL);
184}
185
186MULTIPROCESS_TEST_MAIN(PolicyDenyError) {
187  mach_port_t port = MACH_PORT_NULL;
188  kern_return_t kr = bootstrap_look_up(bootstrap_port, kTestServer,
189      &port);
190  CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, kr);
191  CHECK(port == MACH_PORT_NULL);
192
193  kr = bootstrap_look_up(bootstrap_port, "org.chromium.some_other_server",
194      &port);
195  CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, kr);
196  CHECK(port == MACH_PORT_NULL);
197
198  return 0;
199}
200
201TEST_F(BootstrapSandboxTest, PolicyDenyDummyPort) {
202  BootstrapSandboxPolicy policy(BaselinePolicy());
203  policy.rules[kTestServer] = Rule(POLICY_DENY_DUMMY_PORT);
204  sandbox_->RegisterSandboxPolicy(1, policy);
205
206  RunChildWithPolicy(1, "PolicyDenyDummyPort", NULL);
207}
208
209MULTIPROCESS_TEST_MAIN(PolicyDenyDummyPort) {
210  mach_port_t port = MACH_PORT_NULL;
211  kern_return_t kr = bootstrap_look_up(bootstrap_port, kTestServer,
212      &port);
213  CHECK_EQ(KERN_SUCCESS, kr);
214  CHECK(port != MACH_PORT_NULL);
215  return 0;
216}
217
218struct SubstitutePortAckSend {
219  mach_msg_header_t header;
220  char buf[32];
221};
222
223struct SubstitutePortAckRecv : public SubstitutePortAckSend {
224  mach_msg_trailer_t trailer;
225};
226
227const char kSubstituteAck[] = "Hello, this is doge!";
228
229TEST_F(BootstrapSandboxTest, PolicySubstitutePort) {
230  mach_port_t task = mach_task_self();
231
232  mach_port_t port;
233  ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
234      &port));
235  base::mac::ScopedMachReceiveRight scoped_port(port);
236
237  mach_port_urefs_t send_rights = 0;
238  ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
239      &send_rights));
240  EXPECT_EQ(0u, send_rights);
241
242  ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port,
243      MACH_MSG_TYPE_MAKE_SEND));
244  base::mac::ScopedMachSendRight scoped_port_send(port);
245
246  send_rights = 0;
247  ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
248      &send_rights));
249  EXPECT_EQ(1u, send_rights);
250
251  BootstrapSandboxPolicy policy(BaselinePolicy());
252  policy.rules[kTestServer] = Rule(port);
253  sandbox_->RegisterSandboxPolicy(1, policy);
254
255  RunChildWithPolicy(1, "PolicySubstitutePort", NULL);
256
257  struct SubstitutePortAckRecv msg;
258  bzero(&msg, sizeof(msg));
259  msg.header.msgh_size = sizeof(msg);
260  msg.header.msgh_local_port = port;
261  kern_return_t kr = mach_msg(&msg.header, MACH_RCV_MSG, 0,
262      msg.header.msgh_size, port,
263      TestTimeouts::tiny_timeout().InMilliseconds(), MACH_PORT_NULL);
264  EXPECT_EQ(KERN_SUCCESS, kr);
265
266  send_rights = 0;
267  ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
268      &send_rights));
269  EXPECT_EQ(1u, send_rights);
270
271  EXPECT_EQ(0, strncmp(kSubstituteAck, msg.buf, sizeof(msg.buf)));
272}
273
274MULTIPROCESS_TEST_MAIN(PolicySubstitutePort) {
275  mach_port_t port = MACH_PORT_NULL;
276  kern_return_t kr = bootstrap_look_up(bootstrap_port, kTestServer, &port);
277  CHECK_EQ(KERN_SUCCESS, kr);
278  CHECK(port != MACH_PORT_NULL);
279
280  struct SubstitutePortAckSend msg;
281  bzero(&msg, sizeof(msg));
282  msg.header.msgh_size = sizeof(msg);
283  msg.header.msgh_remote_port = port;
284  msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND);
285  strncpy(msg.buf, kSubstituteAck, sizeof(msg.buf));
286
287  CHECK_EQ(KERN_SUCCESS, mach_msg_send(&msg.header));
288
289  return 0;
290}
291
292TEST_F(BootstrapSandboxTest, ForwardMessageInProcess) {
293  mach_port_t task = mach_task_self();
294
295  mach_port_t port;
296  ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
297      &port));
298  base::mac::ScopedMachReceiveRight scoped_port_recv(port);
299
300  mach_port_urefs_t send_rights = 0;
301  ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
302      &send_rights));
303  EXPECT_EQ(0u, send_rights);
304
305  ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port,
306      MACH_MSG_TYPE_MAKE_SEND));
307  base::mac::ScopedMachSendRight scoped_port_send(port);
308
309  send_rights = 0;
310  ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
311      &send_rights));
312  EXPECT_EQ(1u, send_rights);
313
314  mach_port_t bp;
315  ASSERT_EQ(KERN_SUCCESS, task_get_bootstrap_port(task, &bp));
316  base::mac::ScopedMachSendRight scoped_bp(bp);
317
318  char service_name[] = "org.chromium.sandbox.test.ForwardMessageInProcess";
319#pragma GCC diagnostic push
320#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
321  kern_return_t kr = bootstrap_register(bp, service_name, port);
322#pragma GCC diagnostic pop
323  EXPECT_EQ(KERN_SUCCESS, kr);
324
325  send_rights = 0;
326  ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
327      &send_rights));
328  EXPECT_EQ(1u, send_rights);
329
330  mach_port_t service_port;
331  EXPECT_EQ(KERN_SUCCESS, bootstrap_look_up(bp, service_name, &service_port));
332  base::mac::ScopedMachSendRight scoped_service_port(service_port);
333
334  send_rights = 0;
335  ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
336      &send_rights));
337  // On 10.6, bootstrap_lookup2 may add an extra right to place it in a per-
338  // process cache.
339  if (base::mac::IsOSSnowLeopard())
340    EXPECT_TRUE(send_rights == 3u || send_rights == 2u) << send_rights;
341  else
342    EXPECT_EQ(2u, send_rights);
343}
344
345const char kDefaultRuleTestAllow[] =
346    "org.chromium.sandbox.test.DefaultRuleAllow";
347const char kDefaultRuleTestDeny[] =
348    "org.chromium.sandbox.test.DefaultRuleAllow.Deny";
349
350TEST_F(BootstrapSandboxTest, DefaultRuleAllow) {
351  mach_port_t task = mach_task_self();
352
353  mach_port_t port;
354  ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
355      &port));
356  base::mac::ScopedMachReceiveRight scoped_port_recv(port);
357
358  ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port,
359      MACH_MSG_TYPE_MAKE_SEND));
360  base::mac::ScopedMachSendRight scoped_port_send(port);
361
362  BootstrapSandboxPolicy policy;
363  policy.default_rule = Rule(POLICY_ALLOW);
364  policy.rules[kDefaultRuleTestAllow] = Rule(port);
365  policy.rules[kDefaultRuleTestDeny] = Rule(POLICY_DENY_ERROR);
366  sandbox_->RegisterSandboxPolicy(3, policy);
367
368  base::scoped_nsobject<DistributedNotificationObserver> observer(
369      [[DistributedNotificationObserver alloc] init]);
370
371  int pid = 0;
372  RunChildWithPolicy(3, "DefaultRuleAllow", &pid);
373  EXPECT_GT(pid, 0);
374
375  [observer waitForNotification];
376  EXPECT_EQ(1, [observer receivedCount]);
377  EXPECT_EQ(pid, [[observer object] intValue]);
378
379  struct SubstitutePortAckRecv msg;
380  bzero(&msg, sizeof(msg));
381  msg.header.msgh_size = sizeof(msg);
382  msg.header.msgh_local_port = port;
383  kern_return_t kr = mach_msg(&msg.header, MACH_RCV_MSG, 0,
384      msg.header.msgh_size, port,
385      TestTimeouts::tiny_timeout().InMilliseconds(), MACH_PORT_NULL);
386  EXPECT_EQ(KERN_SUCCESS, kr);
387
388  EXPECT_EQ(0, strncmp(kSubstituteAck, msg.buf, sizeof(msg.buf)));
389}
390
391MULTIPROCESS_TEST_MAIN(DefaultRuleAllow) {
392  [[NSDistributedNotificationCenter defaultCenter]
393      postNotificationName:kTestNotification
394                    object:[NSString stringWithFormat:@"%d", getpid()]];
395
396  mach_port_t port = MACH_PORT_NULL;
397  CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, bootstrap_look_up(bootstrap_port,
398      const_cast<char*>(kDefaultRuleTestDeny), &port));
399  CHECK(port == MACH_PORT_NULL);
400
401  CHECK_EQ(KERN_SUCCESS, bootstrap_look_up(bootstrap_port,
402      const_cast<char*>(kDefaultRuleTestAllow), &port));
403  CHECK(port != MACH_PORT_NULL);
404
405  struct SubstitutePortAckSend msg;
406  bzero(&msg, sizeof(msg));
407  msg.header.msgh_size = sizeof(msg);
408  msg.header.msgh_remote_port = port;
409  msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND);
410  strncpy(msg.buf, kSubstituteAck, sizeof(msg.buf));
411
412  CHECK_EQ(KERN_SUCCESS, mach_msg_send(&msg.header));
413
414  return 0;
415}
416
417}  // namespace sandbox
418